I am using PDFBOX - 1.8.13, it seems that PDF Security is not working as expected. If owner password is set and user password is not, PDFBOX allows to decrypt my PDF File if I don't provide owner password . Please help where I am doing WRONG.
The code for encrypting my pdf file :
PDDocument document = PDDocument.load(new File("/home/dummy/dummy.pdf"),null);
AccessPermission perms = new AccessPermission();
perms.setCanAssembleDocument(false);;
perms.setCanExtractContent(false);
perms.setCanModify(false);
perms.setCanModifyAnnotations(false);
perms.setCanExtractForAccessibility(false);
perms.setCanFillInForm(false);
perms.setCanPrint(false);
perms.setReadOnly();
perms.setCanPrintDegraded(false);
perms.setCanExtractForAccessibility(false);
document.setAllSecurityToBeRemoved(false);
StandardProtectionPolicy policy = new StandardProtectionPolicy("AdminPasswordTest", "", perms);
policy.setPermissions(perms);
document.protect(policy);
document.save("/home/dummy/dummy_secured.pdf");
document.close();
The code for decrypting my PDF
PDDocument doc = PDDocument.load("/home/dummy/dummy_secured.pdf", true);
if (doc.isEncrypted()) { //remove the security before adding protections
doc.decrypt(""); //This should not be DECRYPTED because owner password is not provided
doc.setAllSecurityToBeRemoved(true); //This user is not provided this permissions
}
doc.save("/home/dummy/dummy_decrypted.pdf");
doc.close();
it seems that PDF Security is not working as expected.
In that case you need to adjust your expectations. ;)
This is effectively how PDF password encryption works:
The user password is the password actually used for encryption and decryption.
The owner password allows to access (a pre-processed version of) the user password in the PDF which then can be used to decrypt the document.
The empty user password "" you used for encryption, therefore, is all the password anyone needs to decrypt the PDF.
Using the owner password instead of the user password also allows you to decrypt the PDF (see above, it allows the PDF processor to retrieve the user password to then continue and decrypt the file) and additionally tells the PDF processor that you are owner of the document and, therefore, shall not be restricted by any of the permissions not given in the document at hand.
PDF libraries usually either don't care about the permissions at all (AFAIK PDFBox doesn't) or have a switch to override restrictions due to a missing owner password (e.g. iText).
Thus, encrypting a PDF using an empty user password (to restrict permissions while letting anyone open the file) is an obstacle which is really easy to overcome.
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 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
currently I am doing some development on my own Prestashop website.
When I wanted to add some user authentication mechanism to my project, I find some "key"s of the website in the file named settings.inc.php.
There is a key named _COOKIE_KEY_ which is used to encrypt user's password and other information using MD5 encryption.
I am just wondering, if _COOKIE_KEY_ a very primate key, or, it is a public key and it doesn't matter anyone else would see that? Please if anyone knows about it a little, I will be very grateful for the help.
Thanks a million!
This key is used to salt Customer Passwords in Database. It should remain confidential at all cost!
Here is the encryption function used to store password from class classes/Tools.php:
/**
* Encrypt password
*
* #param string $passwd String to encrypt
*/
public static function encrypt($passwd)
{
return md5(_COOKIE_KEY_.$passwd);
}
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.