Search code examples
azureazure-active-directoryazure-blob-storagesignaturesas-token

How to Prevent SAS URL being used for another file by just changing the filename in the URL, I'm not using Azure SDK


I'm generating a SAS token for every successful blob upload to Azure storage account and able to view file in browser using URL. But when I change the file names in the URL, I'm able to view another file with same token.

How can we prevent this happening without adding unique identifier?

Thank you in advance!

This is what I am doing to generate SAS token for every successful upload.

public class generateURL extends MbJavaComputeNode {

    public void evaluate(MbMessageAssembly inAssembly) throws MbException {
        MbOutputTerminal out = getOutputTerminal("out");
        MbMessage inMessage = inAssembly.getMessage();
        MbMessageAssembly outAssembly = new MbMessageAssembly(inAssembly, 
                inAssembly.getLocalEnvironment(),
                inAssembly.getExceptionList(),
                inAssembly.getMessage());
        MbElement env = inAssembly.getGlobalEnvironment().getRootElement() ;
        String fileName= env.getFirstElementByPath("Variables/fileName").getValueAsString();
        String accountName= env.getFirstElementByPath("Variables/accountName").getValueAsString();
        String containerName= env.getFirstElementByPath("Variables/containerName").getValueAsString();
        String storageKey= env.getFirstElementByPath("Variables/storageKey").getValueAsString();
        //String uniqueIdentifier= env.getFirstElementByPath("Variables/uniqueIdentifier").getValueAsString();
        String resourceUrl = "https://"+accountName+".blob.core.windows.net/"+containerName+"/"+fileName;
        try {
            // create new message as a copy of the input
            MbMessage outMessage = new MbMessage(inMessage);
            outAssembly = new MbMessageAssembly(inAssembly, outMessage);
            // ----------------------------------------------------------
            // Add user code below
            Calendar calendar = Calendar.getInstance();
            Date start = calendar.getTime();
            Date expiry = new Date(start.getTime() + 3600 * 1000); // 1 hour from now
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
            
            
            String signedPermissions = "r"; //read and list
            String signedService = "b";  //blob
            String signedResource = "c";  //service, container, objects
            String canonicalizedResource = "/blob/"+accountName+"/"+containerName+"/"+fileName;
            String startTimeString = (dateFormat.format(start));
            String expiryTimeString = (dateFormat.format(expiry));
            String protocol = "https";
            String signedVersion = "2015-04-05";
            String signedIP = "";
            String rscc = "";  //Cache-Control
            String signedIdentifier = "";  //Cache-Control
            String rscd = "";  //Content-Disposition               
            String rsce = "";  //Content-Encoding
            String rscl = "";  //Content-Language
            String rsct = "binary";  //Content-Type      binary
                
            
            String stringToSign = signedPermissions + "\n" +  
                    startTimeString + "\n" +  
                    expiryTimeString + "\n" +  
                    canonicalizedResource + "\n" +  
                    signedIdentifier + "\n" +  
                    signedIP + "\n" +  
                    protocol + "\n" +  
                    signedVersion + "\n" +  
                    rscc + "\n" +  
                    rscd + "\n" +  
                    rsce + "\n" +  
                    rscl + "\n" +  
                    rsct;

            String signature = generateSasSignature(storageKey, stringToSign);

            try{
            
            String sasToken = 
                    "sv="+signedVersion +
                    "&sr="+signedResource +
                    "&sp="+signedPermissions +
                    "&st="+ URLEncoder.encode(startTimeString, "UTF-8") +
                    "&se="+ URLEncoder.encode(expiryTimeString, "UTF-8") +
                    "&spr="+protocol+
                    "&sig="+ URLEncoder.encode(signature,"UTF-8")+
                    "&rscc="+ URLEncoder.encode(rscc,"UTF-8")+
                    "&rscd="+ URLEncoder.encode(rscd,"UTF-8")+
                    "&rsce="+ URLEncoder.encode(rsce,"UTF-8")+
                    "&rscl="+ URLEncoder.encode(rscl,"UTF-8")+
                    "&rsct="+ URLEncoder.encode(rsct,"UTF-8");
            
            String sasUrl = resourceUrl+"?"+sasToken;    
            System.out.println(resourceUrl+"?"+sasToken);
            String URL = "url";
         // Set the message body to the string you want to pass
            outMessage.getRootElement().createElementAsLastChild(MbElement.TYPE_NAME_VALUE, URL, sasUrl);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        //}
            // End of user code
            // ----------------------------------------------------------
        } catch (MbException e) {
            // Re-throw to allow Broker handling of MbException
            throw e;
        } catch (RuntimeException e) {
            // Re-throw to allow Broker handling of RuntimeException
            throw e;
        } catch (Exception e) {
            // Consider replacing Exception with type(s) thrown by user code
            // Example handling ensures all exceptions are re-thrown to be handled in the flow
            throw new MbUserException(this, "evaluate()", "", "", e.toString(), null);
        }
        // The following should only be changed
        // if not propagating message to the 'out' terminal
        out.propagate(outAssembly);
    }
        
    
    public static String generateSasSignature(String key, String stringToSign) {
        SecretKeySpec secret_key = new SecretKeySpec(Base64.getDecoder().decode(key), "HmacSHA256");
        Encoder encoder = Base64.getEncoder();
        String signature = null;
        Mac sha256_HMAC = null;

        try {
        sha256_HMAC = Mac.getInstance("HmacSHA256");
        sha256_HMAC.init(secret_key);
        signature = new String(encoder.encode(sha256_HMAC.doFinal(stringToSign.getBytes("UTF-8"))));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signature;
    }   
}

Solution

  • The reason you are seeing this behavior is because you are creating an Account SAS which is valid for all blobs in your storage account.

    What you need to do is create a Service SAS for a particular blob. Once you do that, you will only use the SAS URL for that blob only.