I'm hoping for feedback on an authentication system I have designed.
The requirements are to create a closed, single sign on web environment, where by once one of our employees visit one of our web applications, they are asked to sign in with their credentials backed by our LDAP. Once signed in, they retain this login for a set time period, or the browser session, and across all our web applications. Much how Google's web properties work with a google account, but for our internal systems.
The users themselves operate from windows, mac or linux, and some from just tablets, so this authentication environment needs to exist solely online, kerberos with mod_auth_kerb etc aren't going to cut it.
All our current web applications use PHP.
The system I have so far works like below.
There exists one central authentication system, which I will call the Authentication Handler, or "handler" for short, and one or more authentication "client" web apps, which I will call Authentication Requestor, or "requestor" for short. I will call the user and their browser just "user".
Also, for it to work, the requestor needs to be preconfigured at the handler with a unique requestor_id and return URL for that ID.
A private key for handler is generated and a public key given to all requestors before hand too.
user visits requestor
user enters URL for requestor.
no session exists for this user on requestor, so requestor creates a new SESSION_ID in PHP (with the built in session handler and session_start).
the session has no authentication, so requestor will then generate a URL to the configured handler consisting of two parts, an authrequest token and an envelope.
requestor first regenerate a new session_id (session_regenerate_id in PHP) for this auth request for the client.
requestor then generates a random password, in this case using PHP's openssl_random_pseudo_bytes and stores this in the session data
requestor then encrypts the SESSION_ID using AES256 with the random password for the authrequest in PHP using openssl_encrypt.
requestor then generates envelope data by concatenating it's unique requestor_id, a ':', and the base64-encoded random password.
requestor's envelope data is then encrypted using the pre-shared public key for the handler using openssl_public_encrypt in PHP.
requestor then sends a location: header to user that contains the encrypted envelope and the authrequest as URL parameters to handler.
handler will decrypt the envelope using it's private key, and separate the clientID and password.
handler will check the requestor_id to see if we have this configured, if not, will inform the user that the requestor was not recognised.
handler will authenticate the user (in this case, via asking for username and password and checking against LDAP and/or via a preauthenticated session)
handler will then generate a return authtoken to requestor
handler will decrypt the SESSION_ID from authrequest using the decrypted and decoded password from the envelope
handler will encrypt a new string containing SESSION_ID and an ID of the logged in credentials (username, GUID, etc) with the decrypted and decoded password from the envelope
handler then sends a location: header to user that contains the authtoken as URL parameter to a preconfigured URL for this requestor_id.
requestor receives the request, and will decrypt the token using the password in the users session.
requestor will confirm the SESSION_ID matches the users current session, else it will restart authentication.
requestor can then use the returned crednetialID to identify a local user as authenticated.
This process can be repeated across different systems to give a single signon behaviour.
So, questions:
Does anyone know of a pre-existing, standardised authentication system that meets the requirements and preforms similarly to above?
If not, and given that I'm no security expert, is there anything above that would could be broken? i.e. what is/are the weak point(s) of this method?
You could use Kerberos: http://web.mit.edu/kerberos/www/
Or you implement an internal OpenID system: http://openid.net/
There are also several PHP libraries for OpenID: http://openid.net/developers/libraries/
Your base64 encoding of the password before encryption doesn't really make any sense (for me at least), as it is a function mostly used for serializing data that is to be communicated elsewhere. However, after the envelope is encrypted, you have binary data again, so what you might want to do is serialize the ciphertext.
Also, you might want to check out whether it is possible to do hashing of the credentials at the client-side (1000 times iterated and salted MD5 or SHA-* is fine). You could check out RFC2617 (HTTP Digest Access Authentication) for inspiration. This is to counter the passwords' vulnerability while decrypted at the server.
Related
Let's say I have a MySQL database with thousands of user accounts in it. These accounts contain lots of data, but for verification purposes, they each contain a username and a (hashed and salted) password. Now, when a user requests signing in, I will take a username and password from them, transfer it via WSS to a Node.js server then transfer it via HTTPS to a PHP file on another server. On that server I will look up the username in the MySQL database, and if I find an account, I will hash the password and see if it matches that username's password. If they both match, then I want the PHP file to create a "verification token" of sorts, save it (and associate it with the account verified) and send the token back to the Node.js server. I then want the Node.js server to send that token back to the client and for the client to save that token. Now the next time the user connects to the Node.js server via WSS, I want the client to check for an existing token, and if it exists I want it to send that token via WSS to the Node.js server, the Node.js server to send that via HTTPS to a PHP file, and that PHP file to see what account that token belongs to, and complete the sign in...
So I guess my first question would be: Is this a good model? Would this be a good idea, and would this be secure, or should I do this differently?
My second question is: What would be the best way to go about generating this token? Should it be completely random? Should it be a combination of letters+numbers? Should it be a hash of some random values? How should I go about the generation of this "token"?
To clarify, I'm not asking how to build a server or make requests or transfer data or anything of that sort, I'm merely asking what is the most secure way to generate a "token" that can be used as authentication to the same degree that a username+password can be used.
Thanks in advance! I'm sorry if this is a really stupid question.
I think you are describing a JWT. There are several packages implementing this in PHP.
I am developing the PHP based REST api. I have android app which will send some key parameters to server and the server will respond with the data. In this case, the email is the key element to get all the relevant data. If I want to make it secure, I can save password in sharedPreferences and send it at every request. This might make communication secure, but I understand that sharedPreferences are not secure and putting confidential information in them is not recommended. Also, I cannot store a hash of password in sharedPreferences because I am using the password_hash() function in php api which requires password as plain text. So, i have to send request as a plain text password only. What should I do to make it secure?
There's the normal way things like this are done and that is usually with some form of token authentication.
However, you probably want to learn more about security except what you're doing is a toy app only you intend using. With that being said, PHP is very wonderful at handling sessions, so you can consider using a cookie jar in your app and take advantage of sessions. REST purists will probably hang me for that suggestion, but you know what? It works perfectly provided you're gonna be using your API for just your app.
Another quick and easy solution is to do a normal email and password login from your app. On success, return a randomly generated token to the user and also store in a table on your database. This token is what you can store in sharedpref that will be sent along with every request. To make things more interesting, you can allow the token expire after say 10 minutes if it hasn't been used. On your app this will mean the user needs to login again, and generate a new token.
If you're going with the token generation model, you wanna make sure the token is not easily guessable. I usually just use something like this for those kind of random alphanumeric strings:
$randomLongString = hash('sha384', microtime() . uniqid() . bin2hex(random_bytes(10)));
For the expiry, you can record the timestamp the token table was accessed, store it in a column with the same token. If the old timestamp + (expiry time) > timestamp now, then you know the token is expired. Return a 401. If not update the old timestamp with the current timestamp, then return a 200.
These are relatively simple but effective solutions that work for single server setups, and relies on the fact that the user is still the same user. Other things you could do is IP checking, device ID verification before you generate the token. And if any of those things change along the way, you quickly invalidate the token and provide a way for the user to prove they are who they claim they are.
OAuth2 with client_credentials GrantType is the best option in my opinion.
For tokens I would use JWT (which is default in most PHP implementations) which gives you the ability to validate it stateless and makes your auth env more scalable.
You would the authorize your app using a fixed set of client credentials (client_id and optional client_secret) to authorize the user with its username and password against the auth server. The received token is then stored in sharedPreferences for further authorization.
About the login step at Angular 6:
If I did it as the following:
Send username and password to PHP;
Server code check user if exists;
If really exists, we will send a json array again to Angular containing username and user role
Save them in localstorage
And when user try to navigate through the app, we will check these credentials using canActivate guard service.
Do we need to use JWT too to set a token or isn't necessary ?
Its not compulsory to use JWT, If your application requires extra
mission critical security use it.
Using JWT protects data from unwanted modifications before its received by the server. The unwanted modifications may be made by someone intercepting the data or from the user itself .
JWT just sends data to server in encoded format along with signature. So, that modification become little tough or even if made, data is not accepted by server as signature validation fails.
Sample JWT data passed :
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 // header
.eyJrZXkiOiJ2YWwiLCJpYXQiOjE0MjI2MDU0NDV9 // payload
.eUiabuiKv-8PYk2AkGY4Fb5KMZeorYBLw261JPQD5lM
Sample plain/json data
{
"username": "hello",
"full_name" : "Jason Bourne"
}
Here, you can easily see and modify the data passed, and in JWT you can't.
Yes! you need, because :
The token-based authentication systems allow users to enter their username and password in order to obtain a token which allows them to fetch a specific resource - without entering their username and password at each request. Once their token has been obtained, the user can use the token to access specific resources for a set time period.
JWT (pronounced 'jot') is a token based authentication system. It is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature. The JWT is a self-contained token which has authentication information, expire time information, and other user defined claims digitally signed.
Source: JWT (JSON Web Tokens) Are Better Than Session Cookies
more info: JWT
introduction: this link
Implementation example: php-authorization-jwt-json-web-tokens
It is not neccessary, but may come in handy.
If you have single application working on a single webserver you could skip JWT completely and just have a cookie autentication mechanism, so that each JavaScript call to the webserver contains your authentication cookie so your backend can respond with proper user data.
JWT comes in handy though when you have more servers involved. Think of SSO service acting as a glue between multiple related sites, like StackOverflow and others. You just pass the JWT token and each server can safely assume the data was not tampered with and have immediate access to the user's identity, some basic details etc.
I would to implement a secure REST web service for a mobile app (using PHP).
The idea is to avoid the complexity of OAuth, so I've decided to use the HMAC approach.
I've read some articles like this.
Basically what I need to do is this:
[CLIENT] Before making an API request combine a bunch of data it will send (ie. the url params) and hash it with the private key assigned from the server (HASHED_KEY). This data is sent along with some sort of id/key which allows the server to identify who is the sender (ie. client id,or some sort of public id of the user, it does not matter, we'll call it USER_IDENTIFIER). So at the end we have a request URL with HASH_KEY+USER_IDENTIFIER (ie. mywebservice/users/list?hash=[HASH_KEY]&key=[USER_IDENTIFIER]).
[SERVER] Server receive this request, take (using USER_IDENTIFIER) the private key assigned to the user and HASH the same data hashed by client. If both hash keys (generated from client and generated from server) matches request can be trusted and executed.
(We can also avoid replay attacks and add some other security levels but that's the core).
My question is this:
My APIs should also allow user registration.
So there will be a call called /register_user where client should send at least desired username and password.
After that, server should reply with the private key (HASHED_KEY) client will use to encrypt all other requests.
So basically there is a flaw: this first communication is not secure.
How can I handle this?
My idea is to provide a private key for anonymous calls both on server and client which is used to encrypt data when I don't know yet the user identity. It will be used to secure both username+password while making registration call.
Is it okay? Any other solution?
I've read about oAuth, Amazon REST API, HTTP Basic/Digest and so on but can't get it all into "single piece". This is probably the closest situation - Creating an API for mobile applications - Authentication and Authorization
I would like to built API-centric website - service. So (in the beginning) I would have an API in center and website (PHP + MySQL) would connect via cURL, Android and iPhone via their network interfaces. So 3 main clients - 3 API keys. And any other developer could also develop via API interface and they would get their own API key. API actions would be accepted/rejected based on userLevel status, if I'm an admin I can delete anything etc., all other can manipulate only their local (account) data.
First, authorization - should I use oAuth + xAuth or my some-kind-of-my-own implemenation (see http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/RESTAuthentication.html?r=9197)? As I understand, on Amazon service user is == API user (have API key). On my service I need to separate standard users/account (the one who registered on the website) and Developer Accounts (who should have their API key).
So I would firstly need to authorize the API key and then Authenticate the user itself. If I use Amazon's scheme to check developer's API keys (authorize their app), which sheme should I use for user authentication?
I read about getting a token via api.example.org/auth after (via HTTPS, HTTP Basic) posting my username and password and then forward it on every following request. How manage tokens if I'm logged in simultaneously on Android and a website? What about man-in-the-middle-attack if I'm using SSL only on first request (when username and password are transmitted) and just HTTP on every other? Isn't that a problem in this example Password protecting a REST service?
As allways, the best way to protect a key is not to transmit it.
That said, we typically use a scheme, where every "API key" has two parts: A non-secret ID (e.g. 1234) and a secret key (e.g. byte[64]).
If you give out an API key, store it (salted and hashed) in you
service's database.
If you give out user accounts (protected by password), store the
passwords (salted and hashed) in your service's database
Now when a consumer first accesses your API, to connect, have him
Send a "username" parameter ("john.doe" not secret)
Send a "APIkeyID" parameter ("1234", not secret)
and give him back
the salts from your database (In case one of the parameters is wrong,
just give back some repeatable salt - eg.
sha1(username+"notverysecret").
The timestamp of the server
The consumer should store the salt for session duration to keep things fast and smooth, and he should calculate and keep the time offset between client and server.
The consumer should now calculate the salted hashes of API key and password. This way the consumer has the exact same hashes for password and API key, as what is stored in your database, but without anything seceret ever going over the wire.
Now when a consumer subseqently accesses your API, to do real work, have him
Send a "username" parameter ("john.doe" not secret)
Send a "APIkeyID" parameter ("1234", not secret)
Send a "RequestSalt" parameter (byte[64], random, not secret)
Send a "RequestTimestamp" parameter (calculated from client time and known offset)
Send a "RequestToken" parameter (hash(passwordhash+request_salt+request_timestamp+apikeyhash))
The server should not accept timestamps more than say 2 seconds in the past, to make this safe against a replay attack.
The server can now calculate the same hash(passwordhash+request_salt+request_timestamp+apikeyhash) as the client, and be sure, that
the client knows the API key,
the client knows the correct password