DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Convert Excel and CSV Documents to HTML in Java
  • Securely Sign and Manage Documents Digitally With DocuSign and Ballerina
  • A Practical Guide to Securing NodeJS APIs With JWT
  • How to Get Plain Text From Common Documents in Java

Trending

  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  • Comprehensive Guide to Property-Based Testing in Go: Principles and Implementation
  • AI's Dilemma: When to Retrain and When to Unlearn?
  • Unlocking Data with Language: Real-World Applications of Text-to-SQL Interfaces
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Securing Cloud Storage Access: Approach to Limiting Document Access Attempts

Securing Cloud Storage Access: Approach to Limiting Document Access Attempts

Explore how to secure cloud documents and images by limiting access attempts using GUIDs, tokens, and tracking, enhancing security for image rendering.

By 
Amol Gote user avatar
Amol Gote
DZone Core CORE ·
Mar. 21, 24 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
24.2K Views

Join the DZone community and get the full member experience.

Join For Free

In today's digital age, cloud-hosted applications frequently use storage solutions like AWS S3 or Azure Blob Storage for images, documents, and more. Public URLs allow direct access to publicly accessible resources. 

However, sensitive images require protection and are not readily accessible via public URLs. Accessing such an image involves a JWT-protected API endpoint, which returns the needed image. We must pass the JWT token in the header to fetch the image using the GET API. The standard method for rendering these images in HTML uses JavaScript, which binds the byte content from the API to the img src attribute. Though straightforward, this approach might not always be suitable, especially when avoiding JavaScript execution.

Could we simplify the process by assigning a direct URL to the img src attribute to render images without JavaScript and don't need to pass the JWT token in the header? This is possible with pre-signed URLs provided by AWS S3 and Azure Blob Storage, which grant temporary access to private resources by appending a unique, expiring token to the URL. While enhancing security by limiting access time, pre-signed URLs don't restrict the number of access attempts, allowing potentially unlimited access within the time window.

Acknowledging this, a solution is needed that restricts access time for images within the HTML attribute and limits access attempts, ensuring sensitive images are safeguarded against unauthorized distribution.

Time and Attempts Limited Cloud Storage Resource Access

To address this challenge, we developed a solution combining cloud resources and associated database mapping with unique identifiers (GUIDs) and a token system that gets appended to the URL. We employ a GET API for secure image rendering that combines the base URL, document identifier, and token as query parameters. This method circumvents the limitations of embedding tokens in headers for image src attributes. 

Unique Identifier for the Image

For the image for which we have the requirement to be rendered in an HTML document through the image src attribute, we generate a unique identifier for this image and persist the image cloud storage path and associated unique identifier (GUID) in the database; there is a dedicated API which does this functionality. This API is part of the microservice responsible for managing all the cloud documents for us.

Token Management

In a master token table, we define token types with attributes such as description, reusability, expiry, and access limits. Using the token type above, we generate a limited-time use token for the image identifier. Each image identifier is assigned a token, stored in a transaction token table with the image identifier GUID, enabling us to track access attempts. We have a microservice to manage these tokens. We will generate this limited-time token using one of the APIs from that microservice. 

Now we have both a unique identifier for the image and a time use token; based on that, we build the URL for the cloud storage resource something like below:

{{baseUrl}}/v1/document/{{imageIdentifier}}?token={{limitedTimeUseToken}}

Sample URL:

  • https://api.fabrikam.com/v1/document/e8655967-3d85-4a5c-b1a8-bb885cc4b81b?token=d5c68f04-b674-4df8-8729-081fe7a8f6b7

The above URL is for the API endpoint, which will send image bytes as a response once the token is validated. We will be covering this API in detail below.

API To Render Image

This API is straightforward. It will fetch the image cloud storage path based on the document UUID sent and then go to AWS S3 to pull the image. But before it performs all this, it goes through the token validation process through a filter.    

Sometimes, we need to perform certain operations on client requests before they reach the controller. Similarly, we may need to process controller responses before they are returned to clients. We can accomplish this by utilizing filters in Spring web applications. The above URL is an API endpoint in the documents microservice; the call goes through the filter DocAccessTokenFilter before reaching the controller. DocAccessTokenFilter acts as a gatekeeper for incoming requests to our documents or images serving API. This filter intercepts HTTP requests before they reach their intended controller, performing token validation to ensure that the requestor has permission to access the requested resource. 

Below is the implementation of the filter:

Java
 
@Order(1)
public class DocAccessTokenFilter implements Filter {

    private Logger logger = CoreLoggerFactory.getLogger(DocAccessTokenFilter.class);

    private String tokenValidationApiUrl;

    public DocAccessTokenFilter(String dlApiBaseUrl) {
        this.tokenValidationApiUrl = String.format("%s/%s", dlApiBaseUrl, "doctoken/validate");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        final HttpServletRequest req = (HttpServletRequest) request;
        final HttpServletResponse res = (HttpServletResponse) response;
        final String docToken = req.getParameter("token");
        if (StringUtils.isNullOrEmpty(docToken)) {
            sendError(res, "Missing Document Access Token");
        } else {
            RestTemplate restTemplate = new RestTemplate();
            HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
            restTemplate.setRequestFactory(requestFactory);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);

            TokenValidateRequest tknValidateReq = new TokenValidateRequest();
            tknValidateReq.setToken(docToken);

            ResponseEntity<ApiResult> tknValidateResp = null;
            HttpEntity<TokenValidateRequest> tknValidateReqEntity = new HttpEntity<>(tknValidateReq, headers);
            try {
                tknValidateResp = restTemplate.postForEntity(tokenValidationApiUrl, tknValidateReqEntity, ApiResult.class);

                if (tknValidateResp.getStatusCode() == HttpStatus.OK) {
                    logger.warn("Token validation successful");
                    chain.doFilter(request, response);
                } else {
                    sendError(res, "Invalid Token");
                }
            } catch (Exception ex) {
                logger.warn(String.format("Exception while validating token %s", ex.getMessage()));
                sendError(res, "Invalid Token");
            }
        }
    }

    private void sendError(HttpServletResponse response, String errorMsg) throws IOException {
        response.resetBuffer();
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setHeader("Content-Type", "application/json");

        ApiResult result = new ApiResult();
        result.setStatus(HttpStatus.UNAUTHORIZED);
        result.setMessage(errorMsg);
        ObjectMapper mapper = new ObjectMapper();
        String concatenatedMsg = mapper.writeValueAsString(result);
        response.getOutputStream().print(concatenatedMsg);
        response.flushBuffer();
    }
}


In the filter, we take the token query parameter, pass, and validate that token against the service responsible for managing the tokens, including validating the tokens. If this API returns HTTP status 200, then it's a valid token, and in all other cases, it would be treated as an invalid token. In cases where the token is not passed or token validation fails, the filter throws an HTTP 401 unauthorized error code back to the consuming client application. The filter calls another API responsible for validation and managing all tokens. This filter ensures that every request to access a document or image passes through a security checkpoint, verifying that the requestor possesses a valid, unexpired access token. 

We need to configure the filter as part of the Spring Boot application as below:

Java
 
@EnableWebSecurity
@Configuration
@Order(2)
public class DocSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    Environment env;

    @Value("${token.validation.api}")
    String tokenValidationApiUrl;;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .requestMatchers()
                .antMatchers("/api/v1/document/**").and()
                .addFilterBefore(new DocAccessTokenFilter(this.tokenValidationApiUrl),
                        UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests().anyRequest().permitAll();
    }
}


We configure security for a Spring web application, specifically applying token validation for requests accessing document resources. We insert a custom filter to validate access tokens, ensuring that document or image access is securely controlled based on token validation rules of attempts and time.

Conclusion

The solution transcends the limitations of pre-signed URLs with an access control system based on time and attempts, enhancing security for cloud-stored images. It simplifies their integration into HTML documents, especially for generating digital documents with HTML content in mobile applications. An application example includes displaying a user's digital wet ink signature, securely stored in cloud storage and seamlessly embedded without relying on JavaScript.  

API Cloud storage Document HTML JWT (JSON Web Token)

Opinions expressed by DZone contributors are their own.

Related

  • How to Convert Excel and CSV Documents to HTML in Java
  • Securely Sign and Manage Documents Digitally With DocuSign and Ballerina
  • A Practical Guide to Securing NodeJS APIs With JWT
  • How to Get Plain Text From Common Documents in Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!