I am working on a Grails application that uses the excellent Spring Security Plugin. The authentication happens through Oracle Access Manager which protects the application URL. So I just use the PreAuth filter and never had to worry about passwords. Until now.
There is another application that we need to integrate with (that manages freezer Samples and need user access management so that user's don't see someone else's samples) and uses LDAP. The said application exposes an API that takes in a username password and returns data based that user's access (there is no on behalf of user feature).
The problem is that I need to ask users for their passwords and send plain text password to that service. So hashing and encoding needs to be reversible and I cannot just compare hashcodes. Any suggestions on how to manage this in the best way possible?
I was thinking of using a random salt created on server (and cycle that like every 6 hours), encode the password and set it in a short life cookie, and decode it on the server when making the call to the external service. This way a potential attacker will need data from server memory and the cookies from user's system and I don't store plain text password anywhere. Just a naive attempt. Very open to suggestions.
So I hacked one of my apps to work like this:
In the User class: (Spring Security User.groovy)
static transients = ['springSecurityService', 'rawPassword']
//To bypass facebook users who log in via facebook
//we will back up original hashed password string
//log them in then save old password has by calling user.rawPassword=oldHash
//this will update underlying password with string hash value
void setRawPassword(String p) {
password=p
}
Then in the relevant service
//Get old password hash
def oldPassword=user?.password
String authPassword
if (oldPassword) {
def uid = user.password + new UID().toString() + prng.nextLong() + System.currentTimeMillis()
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(uid.getBytes("UTF-8"));
def token1 = hash.encodeBase64()
user.password = token1
//Generate a random password
authPassword = token1
//Update user Password to be random Password
UsernamePasswordAuthenticationToken uat1 = new UsernamePasswordAuthenticationToken(user.username, authPassword, null)
uat1.setDetails(user)
SecurityContext context = SecurityContextHolder.getContext()
//Re-setAuthentication of springSecurity using new Password
context.setAuthentication(uat1)
if (oldPassword) {
//Now we are authenticated let's set back the original Hash as the hash we collected in oldPassword
//just before doing the hack
user.rawPassword = oldPassword
user.save(flush: true)
}
springSecurityService.reauthenticate user.username
}
This is rather an ugly hack to authenticate as a user without changing their set password (in the end) during process changed and changed back again..
I am not recommending it but it may be an easier option than what you have outlined
Related
I am developing a Google Workspace Addon (standalone script) which will make REST API calls to external service and for that purpose it needs to provide an API key.
I request the API key input from a user and then store it in PropertiesService in the following way:
function onSheets(e) {
const userProperties = PropertiesService.getUserProperties();
const saved_api_key = userProperties.getProperty('api_key');
const api_key: string = saved_api_key ? saved_api_key : "";
const builder = CardService.newCardBuilder();
const apiKeyInput = CardService.newTextInput().setTitle('API Key')
.setFieldName('api_key')
.setHint('Enter your API Key')
.setValue(api_key);
const saveApiKey = CardService.newAction().setFunctionName('saveApiKeyFn');
const button = CardService.newTextButton().setText('Save').setOnClickAction(saveApiKey);
const optionsSection = CardService.newCardSection()
.addWidget(apiKeyInput)
.addWidget(button)
builder.addSection(optionsSection);
return builder.build();
}
function saveApiKeyFn(e) {
const api_key = e.formInput.api_key;
const userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('api_key', api_key);
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification()
.setText("API Key saved"))
.build();
}
Since userProperties are scoped to a current user it seems fine. But I have serveral problems with this solution:
Is this really safe? I mean it is stored in plain text so maybe there are ways to retrive it by other mailcious user?
The idea that by mistake I would use getScriptProperties() and thus leak one user's API key to all other users gives me nightmares. It is highly sensitive API key. It would cost a user tons of money if abused.
I read that some user's suggest https://cloud.google.com/secret-manager but I am not sure it's fit for this particular scenario. It would require one more external API call. It is not free. And lastly from what I underestand I would be sort of an owner of all of these secrets since I will be the owner of the Google Cloud project in which this API runs.
All I want is for the users to be able to store their keys safely, so that no one else including me can never access them.
What would you suggest? Thanks!
Is this really safe? I mean it is stored in plain text so maybe there are ways to retrive it by other mailcious user?
Security is relative. There's no such thing as absolute secrecy. Here are some attack scenarios:
Google employees or support may have unrestricted access
If a particular user installed a trigger, that trigger runs as that user and other users, if they can trigger the script and have edit access to the script, will be able to access the keys. A common scenario would be a installed edit trigger in a sheet. User B can access user A, if he can make a edit as well as edit the script. As mentioned in the comments by doubleunary, this is less of a problem in a published add on, as the source code is not accessible or editable.
Encrypting keys is a possibility. But, where would you store the decrypting key? You could ask every user to have a custom password for decrypting the key. But how many times are you going to make a API call? Would they have to enter the key every time? At what point does convenience overtake the need for secrecy?
The idea that by mistake I would use getScriptProperties() and thus leak one user's API key to all other users gives me nightmares. It is highly sensitive API key. It would cost a user tons of money if abused.
That is a possibility, but one that's easily avoidable by careful code review by yourself and your peers.
Those are the scenarios I could think of.
Related:
Securely Storing API Secrets used in Google Apps Script - Published Library
On receiving a GET request in Flask, I connect to a backend database and send the response. Currently, the username and password of the database are stored in an ini file. What is the best way to encrypt the username and password?
Also is it good practice to encrypt the username and password for REST calls ? as I need to decrypt every time on receiving a request
You never store plain password in you database. Instead, you want to store hashes -- the special sum, which can't be decoded, but will produce the same result on same data.
Therefore, you can just apply this function to plain password and compare it to the one on your database
Take a look at bcrypt module:
https://flask-bcrypt.readthedocs.io/en/latest/
On your register method:
pw_hash = bcrypt.generate_password_hash('some_password')
And then you only store pw_hash in your db
On your login method just extract pw_hash from db and compare it:
bcrypt.check_password_hash(pw_hash, 'password_from_request') # returns True or False
At the same time, you can store plain username in DB if you want, there's nothing wrong with it
On your server, you cannot encrypt the username and password to access your database, otherwise you cannot access it.
Usually, you do not put them in a file, but in an environment variable.
Also see the twelve factor app:
https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology
P.S.: For instance, I use batou for deployment (similar to Ansible).
The username and password are both encrypted in a gpg file, so I can check them into version control. But of course, when I deploy the app to production, both values need to be un-encrypted.
https://batou.readthedocs.io/en/stable/
I implemented two factor authentication but by following this tutorial
https://learn.microsoft.com/en-us/aspnet/identity/overview/features-api/two-factor-authentication-using-sms-and-email-with-aspnet-identity
I want to make the code expire after single use.
Right now, user receives the same code during the expiration time (which is set to 5 minutes) completes. Is there a way to make the code single use? I couldn't find anything on this subject.
There is a note in the tutorial that you linked to that says:
The 2FA codes are generated using Time-based One-time Password Algorithm and codes are valid for six minutes. If you take more than six minutes to enter the code, you'll get an Invalid code error message.
So, using this method, you cannot make the code expire after user.
You could, as an addition, keep a store of codes that have been used and check against that store before validating the code. You could allow the codes to expire out of that store after 6 minutes, which is their natural expiry time, but in the meantime use them to reject a second authentication.
Alternatively, you can choose to avoid the TOTP method and generate a random code that you store against your user before you send the SMS or email. Then you can check against that code when the user authenticates with it and delete or invalidate the code at that point. Using TOTP means that you could extend this 2FA to use an authenticator app based flow for the authentication too, which is more secure than SMS or email.
AspNetIdentity does not automatically invalidate used second factor codes, a code is always valid for a six minute window, but there is a workaround for this.
One of the inputs to the token generator is the SecurityStamp, which is stored as part of the user account. Token providers that extend the TotpSecurityStampBasedTokenProvider, like for example the EmailTokenProvider, will use the security stamp when they generate and validate a second factor code.
Thus, you can invalidate all issued tokens by changing the security stamp by calling UserManager.UpdateSecurityStampAsync(userId) after a successful two factor authentication.
There is a side effect that may not be desirable, being that other sessions will get logged out when the security stamp changes.
In the ApplicationSignInManager class, you can override TwoFactorSignInAsync and make the call there:
(Note: This is taken from AspNetIdentity, if you are using a different package, make sure to take TwoFactorSignInAsync from that instead and modify it accordingly.)
public override async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberBrowser)
{
var userId = await GetVerifiedUserIdAsync().WithCurrentCulture();
if (userId == null)
{
return SignInStatus.Failure;
}
var user = await UserManager.FindByIdAsync(userId).WithCurrentCulture();
if (user == null)
{
return SignInStatus.Failure;
}
if (await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())
{
return SignInStatus.LockedOut;
}
if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code).WithCurrentCulture())
{
// When token is verified correctly, clear the access failed count used for lockout
await UserManager.ResetAccessFailedCountAsync(user.Id).WithCurrentCulture();
// Update the security stamp in order to invalidate all issued two factor tokens.
await UserManager.UpdateSecurityStampAsync(user.Id);
await SignInAsync(user, isPersistent, rememberBrowser).WithCurrentCulture();
return SignInStatus.Success;
}
// If the token is incorrect, record the failure which also may cause the user to be locked out
await UserManager.AccessFailedAsync(user.Id).WithCurrentCulture();
return SignInStatus.Failure;
}
If you want only the latest issued code to be valid, you should make the call to UpdateSecurityStampAsync also before any new code is generated.
Say a user submits their credentials using basic authentication. I have a custom message handler that retrieves the credentials from the header:
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
System.Threading.CancellationToken
cancellationToken)
{
try
{
// Request Processing
var headers = request.Headers;
if (headers.Authorization != null && SCHEME.Equals(headers.Authorization.Scheme))
{
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string credentials = encoding.GetString(Convert.FromBase64String(headers.Authorization.Parameter));
string[] parts = credentials.Split(':');
string userId = parts[0].Trim();
string password = parts[1].Trim();
// TODO: Authentication of userId and Pasword against credentials store here
I'm wondering - when I actually authenticate the userId and password here, I'm tempted to compare the plaintext from this method to plaintext stored in the database, but I know this is insecure. Should I be hashing both the plaintext from the header and credentials stored in the database to compare indirectly?
If so, it seems like the credentials are in plaintext in the messagehandler, before they get hashed. Does this present any kind of security vulnerability, or is it ok?
There is data in motion and data at rest. Hashing is typically for data at rest, specifically password kind of things, where the user will be submitting the un-hashed form for you to hash and compare against the authority. If you are worried about clear password stored, hash it and store. Retrieve the password in clear text from the header, hash it and compare against the one in the database. If your concern is sending password over the wire, you should not be using basic authn.
SSL covers you on having the credentials in plaintext as they are passed over the wire.
Passwords should definitely be hashed and salted in the database, and never saved anywhere in plaintext.
Having the passwords in a string variable on the server is really only a problem if you have someone memory-scraping your server. And if that happens, you have bigger problems. If you're concerned about minimizing that window of availability, you can take the password from the header and put it into a byte array instead of a string, so you can clear the byte array as soon as you are finished with it. However, the value is already stored in a string in headers.Authorization.Parameter, so that may not gain you much.
I'm using the Acegi plugin in my Grails app. After a user registers, he is redirected to an action that is protected. Therefore, he is shown the login form.
However, I would prefer if the user was automatically logged in as soon as he completes registration. It seems like I could achieve this by redirecting to the action that the login form uses
redirect(uri:"/j_acegi_security_check?j_username=${username}&j_password=${passed}")
But this would send a HTTP request to the client (and back to the server) which shows the user's password. Is there a way I can login automatically in a secure fashion?
Thanks,
Don
If you generate the controller classes for the spring security plugin (grails generate-registration) you'll see the following lines in RegisterController which does just what you want:
class RegisterController {
def daoAuthenticationProvider
...
def save = {
...
def auth = new AuthToken(person.username, params.passwd)
def authtoken = daoAuthenticationProvider.authenticate(auth)
SecurityContextHolder.context.authentication = authtoken
redirect uri: '/'
}
Be sure that params.passwd is the plain-text password (i.e. not hashed) and it works like a charm.
I haven't tried this with non-test code, but this is the method that I created to log a user in within my integration tests (after building/saving the appropriate users/roles in my test setup):
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsDaoAuthenticationProvider
import org.springframework.security.providers.UsernamePasswordAuthenticationToken
import org.springframework.security.context.SecurityContextHolder
...
def logInAsUser(username, password) {
daoAuthenticationProvider.getUserCache().removeUserFromCache(username)
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password)
SecurityContextHolder.context.authentication = daoAuthenticationProvider.authenticate(token)
}
I construct and inject the authentication token in the security context. There might be a little more that you need to do to get your user logged in and past security, but this would be the start of whatever that is.
I'll actually need to do exactly what you're asking in a week or two for my current app, post back if you figure it out fully before I do :).
This is Burt Beckwith's answer (not mine)
(It was left as a comment by Burt, but I think it deserves to be more prominent)
If you don't have the password, you can load the user via
def user = User.findByUsername(username)
and setting the authority array in the 3-parameter constructor. Create the auths via
GrantedAuthority[] auths = user.authorities.collect { new GrantedAuthorityImpl(it.authority) }
Then you can omit the call to authenticate() and use:
SecurityContextHolder.context.authentication = new UsernamePasswordAuthenticationToken(username, 'unknown', auths)