As a more broad question I would like to ask what is the current best strategy for securing a website login. I know all of the basics, like salting a password, hashing the password, and using SSL to encrypt the transmission, but I feel that may not always be enough. What are the best, "hack-proof" methods out there?
Your points are already the most important ones, additionally you can do this:
Use a slow key derivation function like BCrypt to hash the password.
Add the X-Frame-Options to the HTTP header of your login page, so that the page cannot be shown inside an iframe. This can help against clickjacking. In PHP this would look like that: header('X-Frame-Options: DENY');.
Add the Content-Security-Policy to the HTTP header of your login page. If a browser supports CSP, this can be an effective protection against Cross-Site-Scripting. In PHP it would look like this: header("X-Content-Security-Policy: allow 'self'");.
Regenerate the session id on the login page, to make session fixation more difficult.
Use HTTPS for the whole site, this avoids lots of problems. If you need to switch between HTTPS and HTTP, then use a separate cookie for authentication, have a look at this example.
Should your site be HTTPS only, then you can add the HTTP Strict Transport Security header. The HSTS can prevent users (that already visited your site once), from calling unsecure HTTP pages. This can help against SSL-strip.
Don't store password at all, but use SRP
http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol
Related
I'm writing REST API in PHP and recently I faced with authorization problem. I read a lot about basic authorization, about using private and public keys to create request signature. It is said that using request signature is more secure. But then I have a question:
-How should user will pass public key and generated signature?
I'm thinking about several options:
1) Create custom http header like X-Key, X-Signature
2) Use authorization header with custom scheme, like
AUTHORIZATION: SIGNATURE key='123' signature='abc'
3) Send this values as parameters. But I don't know if it acceptable for methods DELETE and PUT
What would you advice?
p.s. I don't want to implement oAuth
What are the desired properties of authentication scheme? Is this a publicly accessible or an intranet service? Are user accounts linked to something outside of scope of your API (linked 3rd party accounts etc). How are you going to distribute user credentials?
I would probably stick with plain old basic authorization, but encrypt everything at the transport level, making use of HTTPS mandatory. Rolling out your own cryptographic scheme is generally not a good idea. It's easier to fall victim to timing or replay attack than it seems. If you insist on client using a key pair for authentication, you can use HTTPS client certificates (though this is not widely used and maybe somewhat cumbersome solution).
There are a few security concerns about plain-text authentication over TLS. First, if someone implements MITM with forged certificate using either well known CA (maybe a government agency) or CA the client is forced to trust (big evil corporate proxy), they will get credentials. But you can't protect the client from its own environment anyway. Second, basic authentication can be prone to CSRF because browser knows how to do it and can remember credentials if you presented challenge and user filled the form. That's not a big problem if you adhere to REST principles and never allow state-changing GET requests. Also, if you are using JSON, never return arrays.
Is it possible to do optional kerberos authentication?
What I want is: if the client (browser) is not on the domain it is redirected to a username/password web login. Otherwise it will do SPNEGO do Kerberos authentication.
If I just send the WWW-Authenticate: Negotiate header to a non domain browser it just does nothing further.
Is there some option to tell the browser to try something different if it doesn't know how to authenticate? Or do I have to determine if user is part of the domain before sending the "WWW-Authenticate" header?
I haven't found anynone who has solved this publicly and in a standard way. Yes, as mentioned, one could fall back to Basic but that doesn't work for authentication schemes which involve requesting a username and password from a CGI form where, as far as the browser sees things, you're falling back to no authentication if Negotiate fails. Maybe that is suggestive that the authentication scheme is broken? I don't know.
I'll tell you what I know, first. Our site is, effectively, Cosign-protected, so we have a similar problem to you: only specially-configured machines respond to the WWW-Authenticate header, so by default we must send all users to our Cosign login page. The trick is that the Cosign server also allows authenticated GSSAPI/Kerberos hosts to complete the authentication process without entering login details, but only on certain browsers, by means of a workaround.
This workaround consists simply of a block of JavaScript within the login page which attempts a HEAD of an SPNEGO-protected resource; if successful, the script redirects the browser to an SPNEGO-protected version of the same page, which grants appropriate Cosign cookies and completes the process without password entry. If the browser lacks any one of JavaScript, Kerberos support or adequate credentials, then the user will see the cosign login page as usual.
So, the above alone might count as an answer to your question; personally though I don't think this goes far enough and what follows is more of a discussion...
The above seems unsatisfactory as it insists that any connecting user agent supports either JavaScript (unlikely to be the case for text-based browsers and HTTP client libraries) or knowledge of the arbitrary path to which we redirect Kerberos-capable users (useless to anything which has not been hard-coded for our site). I've come to the conclusion that there might be a better workaround, or if not, a gap where a standard should be. The best practical suggestion I have is this:
A normal part of the SPNEGO process is that the client attempt to retrieve a page whose initial response is an HTTP 401 but with the header WWW-Authenticate: Negotiate. This is the cue for a GSSAPI/Kerberised client to respond appropriately; a "regular" client will simply display the error page. Perhaps the solution is simply to modify the Cosign server to deliver the human-friendly login page as part of this error response?
It might be technically difficult with off-the-shelf Apache and modules, and might go against various standards (or at least principles). I'm no expert on the systems involved, so can only speculate unless (or until) I get a chance to try it out...
Send additionally WWW-Authenticate: Basic for username/password challenge.
It depends on the application server, for example in JBoss you can set <auth-method>SPNEGO,FORM</auth-method> in web.xml and it should fall back to a login form "in cases where Kerberos/SPNEGO tokens are not present":
See https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.2/html/how_to_set_up_sso_with_kerberos/additional_features
Suppose I have a form on a page at this location...
http://mydomain.com/myform.htm
And the form looks like this...
<form method="post" action="https://secure.otherdomain.com/handleform.php">
....
</form>
Assuming that there is a valid SSL cert installed on the server which receives this form submission will the contents of that form submission be encrypted?
The POST request will be transmitted over HTTPS (so encrypted if configured properly). Submitting a form from a page obtained over plain HTTP to an HTTPS page is bad practice. The initial page should also be served over HTTPS. The reason for this is that a MITM attacker could intercept the response that loads the page with the form and replace the link to point to another target.
See the first rule here (of course, not specific to login pages):
https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Use_TLS_for_All_Login_Pages_and_All_Authenticated_Pages
Rule - Use TLS for All Login Pages and All Authenticated Pages
The login page and all subsequent authenticated pages must be
exclusively accessed over TLS. The initial login page, referred to as
the "login landing page", must be served over TLS. Failure to utilize
TLS for the login landing page allows an attacker to modify the login
form action, causing the user's credentials to be posted to an
arbitrary location. Failure to utilize TLS for authenticated pages
after the login enables an attacker to view the unencrypted session ID
and compromise the user's authenticated session.
Assuming a valid SSL/TLS session can be negotiated between the server and the client, then yes. This means that the client must be willing to trust whatever certificate the server presents and that the two parties can negotiate a mutually-agreeable cipher set (what algorithms to use, etc). There are plenty of configuration options you can set to alter what is allowed, but in a "normal" implementation where you don't go messing around with requiring a specific, non-normal, algorithm, requiring client-side certificate authentication, etc, everything should work just fine and you'll have a protected session...and if it fails for some reason, you'll know as your client will receive an error about what went wrong.
Note that, in general, while you can do this, and the transmission would be encrypted, you generally should not. Having an unencrypted/protected page submit to one leaves you vulnerable to a couple types of Man in the Middle attacks. You can see the OWASP article on this, and why it's bad, here.
Yes. It will be transmitted securely.
How can I implement a cookie based single sign on without a sso server?
I would to share the user logged in across multiple applications using
only a cookie on the browser.
In my mind it's working like this:
user logs in an application
the application verifies the credentials and then it setting up a cookie on
the browser storing the username (that could be coded with a private key)
if the user opens another application, it searches the cookie and reads
the username on the value (using the key for decode the string)
In this solution a user may see the browser cookie (of a another user)
and take the string codified of the username. Then he could adding it on
an own cookie (no good!).
There's some secure way to do this? With a timestamp based control or
something like this?
Thanks in advance.
Bye
P.S.
I know that my english isn't very well.. sorry for this!
This is impossible. Cookies are unique to each domain, and one domain cannot read another domain's cookies.
I think the answer comes a little late, but maybe I can help someone.
You can have a cookie / localStorage in an intermediate domain connected to the home page using an iframe
1) Login
The login form in any of your domains deposits the identification token in a cookie on sso.domain.com by an event (postMessage)
2) Verification
domain1 and domain2 include a iframe pointing to sso.domain.com, which reads the token and notifies the home page
To simplify development, we have released recently a cross domain SSO with JWT at https://github.com/Aralink/ssojwt
There is a simple solution without using an sso server, but not with 1 common cookie, as we know that cookie's are not shared between domains.
When the user authenticates on site-a.com, you set a cookie on site-a.com domain. Then on site-b.com, you link a dynamic javascript from site-a.com, generated by server side script (php, etc) who has access to the created cookie, and then copy the same cookie on site-b.com on the client-side using js. Now both sites have the same cookie, without the need of asking the user to re-login.
You may encrypt/encode the cookie value using a method that both site-a and site-b knows how to decode, so that site-b will be able to validate his cookie copy. Use a common shared secret that without it will be impossible to encode or decode.
You see that on the 1st page load of site-b.com, the cookie is not present, therefore if you see necessary, you may want to do a page reload after setting the cookie.
I have done something similar. There is a PHP application where the user logs in, the system contact a web service and then the service checks the user's credentials on the Active Directory. When the user is authenticated, his PHP session is stored in the DB. Another web application can read the PHP session from the cookies and uery a web service in the PHP applicaiton, the PHP application check the session in the database and return the user id. In this way I have a SSO using SOA.
Do not rely on the user id stored in the browser, is a security error, at least encrypt the id.
The best solution would be to put the login form and session storage in the same application, then this application can provide services to other applications.
And use HTTPS for the kind of infomation exchange.
The cookies can be read only if the belongs to the same domain, for instance:
intranet.example.com
crm.example.com
example.com/erp
You can access cookies across subdomains, but I do not think using browser cookies is a great solution. You really don't need a "SSO server" to implement a single sign-on. It is fairly easy to come up with a payload that both applications recognize. I have seen custom SSO solutions that transmit the payload using XML over HTTPS.
Here is a solution (which will hopefully get heavily scrutinized by security gurus on here):
Have each domain store user data in a similar cookie, and when a user want to jump from one domain to another without authenticating themselves on the new domain, provide a "jumplink" with an encrypted token in the query string. The new domain would decrypt the cookie, and figure out who the user is, then issue them a new cookie for that domain. You would want the "jumplink" to have a very short expiration date, so I would not generate them right into the page, but generate links to a "jumplink" generator and re-director.
This might not be necessary, but the receiving page for the "jumplink" could make a web service call back to the originating domain, to verify the authenticity of the encrypted token and the whether it's expired.
I think this solution would be susceptible to man-in-the-middle attacks (not sure if it would be more so than other auth mechanisms which are currently popular), but you could incorporate a client MAC address and IP address into the encrypted token for extra security.
Background:
I'm designing the authentication scheme for a REST web service. This doesn't "really" need to be secure (it's more of a personal project) but I want to make it as secure as possible as an exercise/learning experience. I don't want to use SSL since I don't want the hassle and, mostly, the expense of setting it up.
These SO questions were especially useful to get me started:
RESTful Authentication
Best Practices for securing a REST API / web service
Examples of the best SOAP/REST/RPC web APIs? And why do you like them? And what’s wrong with them?
I'm thinking of using a simplified version of Amazon S3's authentication (I like OAuth but it seems too complicated for my needs). I'm adding a randomly generated nonce, supplied by the server, to the request, to prevent replay attacks.
To get to the question:
Both S3 and OAuth rely on signing the request URL along with a few selected headers. Neither of them sign the request body for POST or PUT requests. Isn't this vulnerable to a man-in-the-middle attack, which keeps the url and headers and replaces the request body with any data the attacker wants?
It seems like I can guard against this by including a hash of the request body in the string that gets signed. Is this secure?
A previous answer only mentioned SSL in the context of data transfer and didn't actually cover authentication.
You're really asking about securely authenticating REST API clients. Unless you're using TLS client authentication, SSL alone is NOT a viable authentication mechanism for a REST API. SSL without client authc only authenticates the server, which is irrelevant for most REST APIs because you really want to authenticate the client.
If you don't use TLS client authentication, you'll need to use something like a digest-based authentication scheme (like Amazon Web Service's custom scheme) or OAuth 1.0a or even HTTP Basic authentication (but over SSL only).
These schemes authenticate that the request was sent by someone expected. TLS (SSL) (without client authentication) ensures that the data sent over the wire remains untampered. They are separate - but complementary - concerns.
For those interested, I've expanded on an SO question about HTTP Authentication Schemes and how they work.
REST means working with the standards of the web, and the standard for "secure" transfer on the web is SSL. Anything else is going to be kind of funky and require extra deployment effort for clients, which will have to have encryption libraries available.
Once you commit to SSL, there's really nothing fancy required for authentication in principle. You can again go with web standards and use HTTP Basic auth (username and secret token sent along with each request) as it's much simpler than an elaborate signing protocol, and still effective in the context of a secure connection. You just need to be sure the password never goes over plain text; so if the password is ever received over a plain text connection, you might even disable the password and mail the developer. You should also ensure the credentials aren't logged anywhere upon receipt, just as you wouldn't log a regular password.
HTTP Digest is a safer approach as it prevents the secret token being passed along; instead, it's a hash the server can verify on the other end. Though it may be overkill for less sensitive applications if you've taken the precautions mentioned above. After all, the user's password is already transmitted in plain-text when they log in (unless you're doing some fancy JavaScript encryption in the browser), and likewise their cookies on each request.
Note that with APIs, it's better for the client to be passing tokens - randomly generated strings - instead of the password the developer logs into the website with. So the developer should be able to log into your site and generate new tokens that can be used for API verification.
The main reason to use a token is that it can be replaced if it's compromised, whereas if the password is compromised, the owner could log into the developer's account and do anything they want with it. A further advantage of tokens is you can issue multiple tokens to the same developers. Perhaps because they have multiple apps or because they want tokens with different access levels.
(Updated to cover implications of making the connection SSL-only.)
Or you could use the known solution to this problem and use SSL. Self-signed certs are free and its a personal project right?
If you require the hash of the body as one of the parameters in the URL and that URL is signed via a private key, then a man-in-the-middle attack would only be able to replace the body with content that would generate the same hash. Easy to do with MD5 hash values now at least and when SHA-1 is broken, well, you get the picture.
To secure the body from tampering, you would need to require a signature of the body, which a man-in-the-middle attack would be less likely to be able to break since they wouldn't know the private key that generates the signature.
In fact, the original S3 auth does allow for the content to be signed, albeit with a weak MD5 signature. You can simply enforce their optional practice of including a Content-MD5 header in the HMAC (string to be signed).
http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
Their new v4 authentication scheme is more secure.
http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
Remember that your suggestions makes it difficult for clients to communicate with the server. They need to understand your innovative solution and encrypt the data accordingly, this model is not so good for public API (unless you are amazon\yahoo\google..).
Anyways, if you must encrypt the body content I would suggest you to check out existing standards and solutions like:
XML encryption (W3C standard)
XML Security