JavaBlog.fr / Java.lu DEVELOPMENT,Java Java : Avoid Unauthorized File Downloading, Use of Token, Download File Servlet

Java : Avoid Unauthorized File Downloading, Use of Token, Download File Servlet

Hello,

I would like to expose a home-made solution in order to avoid the unauthorized file downloading via the generation and use of unique token (PART I). The second part (PART II) contains an example of download file servlet allowing several methods of writing file’s content to client.
 
PART I
Here, a singleton named AntiDownloadSingleton used on server side to:

  • generate an unique token,
  • store the available tokens,
  • use/check if a token is available with automatically token’s removing

 

01package com.ho.file.antidownload;
02 
03import java.util.Map;
04import java.util.concurrent.ConcurrentHashMap;
05 
06/**
07 * Singleton used in SERVER side in order to avoid the unauthorized of file downloading via the generation and use of unique token.
08 */
09public class AntiDownloadSingleton {
10 
11    // ---------------------------------------------------- PRIVATE ATTRIBUTES
12    private static AntiDownloadSingleton instance = null;
13    private Map<String, Token> tokens = null;
14 
15    // ---------------------------------------------------------- CONSTRUCTORS
16    private AntiDownloadSingleton(){
17        tokens=new ConcurrentHashMap<String, Token>();
18    }
19 
20    public static synchronized AntiDownloadSingleton getInstance(){
21        if (instance==null){
22            instance = new AntiDownloadSingleton();
23        }
24        return instance;
25    }
26 
27    // ------------------------------------------------------ PUBLIC FUNCTIONS
28    public String generateToken(String userName) {
29        Token token = new Token(java.util.UUID.randomUUID().toString(), userName);
30        tokens.put(token.getId(), token);
31        return token.getId();
32    }
33 
34    public boolean useToken(String id, String userName){
35        if (id!=null && userName!=null && tokens.containsKey(id) && userName.equals(tokens.containsKey(id).getUserName())){
36            System.out.println("Use of token : "+id);
37            tokens.remove(id);
38            return true;
39        }else{
40            System.out.println("An abnormal attempt to load with the token : "+id);
41        }
42        return false;
43    }
44 
45    public Token getToken(String id){
46        Token token = null;
47        if (id!=null && tokens.containsKey(id)){
48            token = tokens.get(id);
49        }
50        return token;
51    }
52 
53    // --------------------------------------------------------- INNER CLASSES
54    private class Token{
55        private String id;
56        private String userName;
57        public Token(String id, String userName) {
58            this.id = id;
59            this.userName = userName;
60        }
61        public void setId(String id) { this.id = id; }
62        public String getId() { return id; }
63        public void setUserName(String userName) { this.userName = userName; }
64        public String getUserName() { return userName ; }
65    }
66}

 

 

So, the kinematics of events is:

  1. Generation of unique token on server side:
    1//Generate unique token for authorized file download
    2String tokenId = AntiDownloadSingleton.getInstance().generateToken(username);
  2. Returning of token value from server to client:
    1public ModelAndView handleLoad(HttpServletRequest request, HttpServletResponse response) throws Exception {
    2    //...
    3    // Store the data in an request attribute
    4    docDetails.setDownloadToken(tokenId)
    5    request.setAttribute("REQ_CLIENT_DATA", docDetails);
    6    return new ModelAndView("/docdetails");
    7}
  3. Sending of token value from client to server in parameter of an file download request from client to server via a POST hidden field or URL GET parameter:
    1<input type="hidden" name="token" value="ddsfsdf4zerez4rzer4zezrze4"/>
    2 
    3http://mywebserver:8080/downloadFile?token=ddsfsdf4zerez4rzer4zezrze4&docId=123456
  4. Check/use of received token on server side:
    01String token = req.getParameter("token");
    02 
    03HttpSession session = req.getSession();
    04String userName = (String) session.getAttribute("userName");
    05 
    06//...
    07if (!AntiDownloadSingleton.getInstance().useToken(token, userName)){
    08    logger.debug("Echec sur "+documentId+", "+token);
    09    sendErrorWeb(req, resp, HttpServletResponse.SC_BAD_REQUEST, "The file downloading is unauthorized.");
    10    return;
    11}
    12//...
    13 
    14private void sendError(HttpServletResponse resp, int codeHttp, String text) throws IOException {
    15    resp.sendError(codeHttp, text);
    16}
    17 
    18private void sendErrorWeb(HttpServletRequest req, HttpServletResponse resp, int scBadRequest, String msg)  {
    19    try {
    20        if (isIE(req)){
    21            sendErrorPdf(resp, msg); // Send a PDF containing a ERROR message
    22        }else{
    23            resp.sendRedirect("/myapplication/downloadfileerror.do?action=handleError&msg="+msg);
    24        }
    25    } catch (Exception e) {
    26        try {
    27            sendError(resp, scBadRequest, msg);
    28        } catch (IOException ioe) {
    29            logger.error("ERROR : ",e);
    30        }
    31    }
    32}
    33     
    34private boolean isIE(HttpServletRequest req) {
    35    boolean navigatorIE = false;
    36    String userAgent = req.getHeader("User-Agent");
    37    if (userAgent!=null){
    38        navigatorIE = userAgent.toLowerCase().indexOf("msie")>=0;
    39    }
    40    return navigatorIE;
    41}

 


 
PART II
Some source code of a download file servlet:

01// HttpServletRequest req
02// HttpServletResponse resp
03// PDDocument pdfDocument
04 
05String documentId = req.getParameter("documentId");
06 
07ServletOutputStream op = resp.getOutputStream();
08 
09resp.setContentType("application/pdf");
10 
11// STEP  : Write file stream in a PipedOutputStream/PipedInputStream
12PipedInputStream inStream = pipePdfDocumentToOutStream(pdfDocument);
13             
14// STEP 2 : write InputStream to HTTP response's OuputStream
15writeInputStreamToOutputStream(op, inStream);

 
 
…This method is used to write PDF stream in the PipedOutputStream via distinct Thread. The PipedOutputStream is connected to a PipedInputStream in order to create communication pipe.

01/*
02* Stream manipulation
03*/
04private PipedInputStream pipePdfDocumentToOutStream (final PDDocument pdfDocument) throws Exception {
05    // Write PDF stream in the PipedOutputStream via distinct Thread
06    // The PipedOutputStream is connected to a PipedInputStream in order to create communication pipe
07    final PipedInputStream inStream = new PipedInputStream();
08    final PipedOutputStream outStream = new PipedOutputStream(inStream);
09 
10    try {
11        // Creation of Writer Thread of PDF stream to PipedOutputStream
12        new Thread(
13            new Runnable(){
14                    public void run(){
15                    try {
16                    pdfDocument.save(outStream); // write to PipedOutputStream
17                } catch (IOException e) {
18                    throw new RuntimeException("IO exception in the Writer Thread", e);
19                }
20                }
21            }
22        ).start();
23    } catch (RuntimeException e){
24        throw new Exception(e.getMessage());
25    }
26 
27    // Return PipedInputStream
28    return inStream;
29}

 
 
…The last point concerns methods of writing file’s content to client:

  • org.apache.commons.compress.utils.IOUtils.copy(…)
  • org.apache.commons.io.IOUtils.copy(…)
  • org.apache.commons.io.IOUtils.copyLarge(…)
  • org.apache.commons.compress.utils.IOUtils.copy(…, int buffersize=BUFFER_LENGTH)
  • Write InputStream of the file content to the OutputStream of HTTP response with buffersize=BUFFER_LENGTH
01private void writeInputStreamToOutputStream(OutputStream myOutputStream, InputStream fileStream) throws IOException {
02    InputStream is = null;
03    BufferedInputStream bfis = null;
04    int MODE_SENDING = 1;
05             
06    try{
07        is = fileStream;
08         
09        try{
10            if(MODE_SENDING == 1){ // org.apache.commons.compress.utils.IOUtils.copy(...)
11                org.apache.commons.compress.utils.IOUtils.copy(is, myOutputStream);
12 
13            }else if(MODE_SENDING == 2){ // org.apache.commons.io.IOUtils.copy(...)
14                org.apache.commons.io.IOUtils.copy(is, myOutputStream);
15 
16            }else if(MODE_SENDING == 3){ // org.apache.commons.io.IOUtils.copyLarge(...)
17                org.apache.commons.io.IOUtils.copyLarge(is, myOutputStream);
18                     
19            }else if(MODE_SENDING == 4){ // org.apache.commons.compress.utils.IOUtils.copy(..., int buffersize=BUFFER_LENGTH)
20                org.apache.commons.compress.utils.IOUtils.copy(is, myOutputStream, BUFFER_LENGTH);
21 
22            }else{ // Write InputStream of the file content to the OutputStream of HTTP response with buffersize=BUFFER_LENGTH
23                int counter=0;
24                int bytesRead=0;
25                bfis = new BufferedInputStream(is);
26                byte[] buffer = new byte[BUFFER_LENGTH];
27                while ((bytesRead = bfis.read(buffer)) != -1) {
28                    myOutputStream.write(buffer, 0, bytesRead);
29                    counter++;
30                }
31            }
32        }catch(IOException ex){
33            throw ex;
34        }
35    }finally{
36        //try{if(is!=null)is.close();}catch(Throwable ignore){}
37        //try{if(bfis!=null)bfis.close();}catch(Throwable ignore){}
38        //try{if(fileStream!=null)fileStream.close();}catch(Throwable ignore){}
39    }
40}

 
 

That’s all!!!

Huseyin OZVEREN

Leave a Reply

Your email address will not be published.

Time limit is exhausted. Please reload CAPTCHA.

Related Post