When you add Persona support to your website, Persona takes on as much of the security burden as it can. However, some aspects of security can only be handled by your website. They're listed below.
Essential practices
Verify assertions on your server
When using Persona, identity assertions are passed into the onlogin
function passed to navigator.id.watch()
. You should always pass the assertion to your server for verification, and only your server should decide to grant the user additional permissions based on the verification result:
// Inside navigator.id.watch({ ... onlogin: function(assertion) { // A user wants to log in! Here you need to: // 1. Send the assertion to your backend for verification and to create a session. // 2. Update your UI. },
If you try to verify the assertion using the JavaScript executing in the user's browser, then a malicious user will be able to impersonate a legitimate user of your site by locally injecting code and subverting your JavaScript. This is possible because you're not fully in control of the user's browser, where the code executes.
Again, you should always pass the assertion to your server for verification. Even if you're using the remote verification API.
Explicitly specify the audience parameter
To verify an assertion, you may issue a POST request to https://verifier.login.persona.org/verify
. The request includes a parameter called audience
:
assertion=<ASSERTION>&audience=https://mysite.com:443"
The audience
parameter is required. You should always specify the audience explicitly in your code, or in your code's configuration. Specifically:
- Do not trust the Host header sent by the user's browser.
- Do not trust an explicit parameter sent by the user's browser, but generated by your JavaScript using, e.g.
document.location
.
If you trust the user's browser to tell you the audience, then it becomes possible for a malicious web site to reuse assertions for its web site to log into your web site.
Verify SSL certificates
To verify an assertion, you may issue a POST request to https://verifier.login.persona.org/verify
. You must ensure that your HTTPS request verifies the certificate sent from the server against a trusted root certificate. If you don't, then an attacker could pose as verifier.login.persona.org
and issue false verifications.
Check that the library you are using to make the request verifies certificates correctly, and that you are initializing it with the appropriate root certificate(s).
For example, Python 2.7's standard urllib2 module does not validate server certificates. Instead, we recommend using the "requests" or "urllib3" modules in Python 2.x, or the standard http.client.HTTPSConnection
class in Python 3.x. For Perl, ensure that you are using at least version 6.0 of libwww-perl
. Depending on the language, library, and operating system that you're using, you may need to supply either a list of trusted CA roots or the single CA used by verifier.login.persona.org
.
Implement CSRF protection
In a CSRF (Cross-Site Request Forgery) login attack, an attacker uses a cross-site request forgery to log the user into a web site using the attacker's credentials.
For example: a user visits a malicious web site containing a form
element. The form's action
attribute is set to an HTTP POST request to https://www.google.com/login, supplying the attacker's username and password. When the user submits the form, the request is sent to Google, the login succeeds and the Google server sets a cookie in the user's browser. Now the user's unknowingly logged into the attacker's Google account.
The attack can be used to gather sensitive information about the user. For example, Google's Web History feature logs all the user's Google search terms. If a user is logged into the attacker's Google account and the attacker has Web History enabled, then the user is giving the attacker all this information.
CSRF login attacks, and potential defenses against them, are documented more fully in Robust Defenses for Cross-Site Request Forgery (PDF). They're not specific to Persona: most login mechanisms are potentially vulnerable to them.
There are a variety of techniques which can be used to protect a site from CSRF login attacks, which are documented more fully in the study above.
One approach is to create a secret identifier in the server, shared with the browser, and require the browser to supply it when making login requests. For example:
- As soon as the user lands on your site, before they try to log in, create a session for them on the server. Store the session ID in a browser cookie.
- On the server, generate a random string of at least 10 alphanumeric characters. A randomly generated UUID is a good option. This is the CSRF token. Store it in the session.
- Deliver the CSRF token to the browser by either embedding it in JavaScript or HTML as a hidden form variable.
- Ensure that the AJAX submission or form POST includes the CSRF token.
- On the server side, before accepting an assertion, check that the submitted CSRF token matches the session-stored CSRF token.
Enhancements
Content Security Policy (CSP)
Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware.
If you use CSP on your site, you may need to tweak your policy to enable Persona. Depending on your policy, you may need to:
- Remove inline
javascript:
URIs and replace them with code loaded from an additional script file. The file can look up elements based on their ID, and then attach to the element by settingonclick
or callingaddEventListener()
. - Allow
https://login.persona.org
as both ascript-src
andframe-src
so that your site can load the remoteinclude.js
file and that file can communicate with the fallback Persona implementation.
An example Apache configuration might include:
Header set X-Content-Security-Policy: "default-src 'self'; frame-src 'self' https://login.persona.org ; script-src 'self' https://login.persona.org"