From 2b3003a84f9604fac83e942e895694fec675c78d Mon Sep 17 00:00:00 2001 From: Oussama Messeguem Date: Fri, 28 Feb 2020 09:58:35 +0000 Subject: [PATCH] MNT-21118: quickshare xss prevention (#536) * MNT-21118: quickshare xss prevention The selected way to prevent from xss attacks is forcing browsers to download files by adding Content-Disposition to the response headers as it is done in V1. Forcing browsers to download files will always be the true for QuickShareContentGet, QuickShareThumbnailContentGet extends QuickShareContentGet, therefore the attach value will be overridden according to the url parameter "a". In the test, the thumbnail must be generated by a logged in user before sharing the link to the document. --- .../quickshare/QuickShareContentGet.java | 64 +++++++++---------- .../QuickShareThumbnailContentGet.java | 57 +++++++++-------- .../quickshare/QuickShareRestApiTest.java | 24 ++++++- 3 files changed, 85 insertions(+), 60 deletions(-) diff --git a/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java b/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java index c735f3f724..2942986036 100644 --- a/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java +++ b/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.web.scripts.quickshare; import java.io.IOException; @@ -135,8 +135,11 @@ public class QuickShareContentGet extends ContentGet implements ServletContextAw { throw new InvalidNodeRefException(nodeRef); } - - executeImpl(nodeRef, params, req, res, null); + + // MNT-21118 (XSS prevention) + // Force the attachment in case of asking for the content file only + // (will be overridden for thumbnails) + executeImpl(nodeRef, params, req, res, null, true); return null; } @@ -160,7 +163,7 @@ public class QuickShareContentGet extends ContentGet implements ServletContextAw } } - protected void executeImpl(NodeRef nodeRef, Map templateVars, WebScriptRequest req, WebScriptResponse res, Map model) throws IOException + protected void executeImpl(NodeRef nodeRef, Map templateVars, WebScriptRequest req, WebScriptResponse res, Map model, boolean attach) throws IOException { // render content QName propertyQName = ContentModel.PROP_CONTENT; @@ -177,10 +180,7 @@ public class QuickShareContentGet extends ContentGet implements ServletContextAw propertyQName = QName.createQName(propertyName, namespaceService); } } - - // determine attachment - boolean attach = Boolean.valueOf(req.getParameter("a")); - + // Stream the content streamContentLocal(req, res, nodeRef, attach, propertyQName, model); } diff --git a/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java b/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java index 66540b87a8..3706121f4f 100644 --- a/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java +++ b/src/main/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.web.scripts.quickshare; import java.io.IOException; @@ -81,7 +81,7 @@ public class QuickShareThumbnailContentGet extends QuickShareContentGet } @Override - protected void executeImpl(NodeRef nodeRef, Map templateVars, WebScriptRequest req, WebScriptResponse res, Map model) throws IOException + protected void executeImpl(NodeRef nodeRef, Map templateVars, WebScriptRequest req, WebScriptResponse res, Map model, boolean attach) throws IOException { String thumbnailName = templateVars.get("thumbnailname"); if (thumbnailName == null) @@ -187,8 +187,11 @@ public class QuickShareThumbnailContentGet extends QuickShareContentGet } } } + + // determine attachment + attach = Boolean.valueOf(req.getParameter("a")); - super.executeImpl(thumbnailNodeRef, templateVars, req, res, model); + super.executeImpl(thumbnailNodeRef, templateVars, req, res, model, attach); if (logger.isDebugEnabled()) { diff --git a/src/test/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java b/src/test/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java index 41b69c3a86..711f3a3868 100644 --- a/src/test/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java +++ b/src/test/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java @@ -108,7 +108,7 @@ public class QuickShareRestApiTest extends BaseWebScriptTest private final static String TEST_MIMETYPE_JPEG = MimetypeMap.MIMETYPE_IMAGE_JPEG; private final static String TEST_MIMETYPE_PNG = MimetypeMap.MIMETYPE_IMAGE_PNG; private static File quickFile = null; - + private MutableAuthenticationService authenticationService; private AuthenticationComponent authenticationComponent; private NodeService nodeService; @@ -357,6 +357,28 @@ public class QuickShareRestApiTest extends BaseWebScriptTest assertFalse(nodeService.hasAspect(copyNodeRef, QuickShareModel.ASPECT_QSHARE)); } + + public void testContentDispositionInResponseHeader() throws IOException, JSONException + { + checkTransformer(); + + String testNodeRef_3 = testNode.toString().replace("://", "/"); + + // Thumbnail creation by user one to genuinely create the thumbnail and allow the sharedId to get it + sendRequest(new GetRequest(AUTH_CONTENT_THUMBNAIL_URL.replace("{node_ref_3}", testNodeRef_3).replace("{thumbnailname}", "doclib")), 200, USER_ONE); + + Response rsp = sendRequest(new PostRequest(SHARE_URL.replace("{node_ref_3}", testNodeRef_3), "", APPLICATION_JSON), 200, USER_ONE); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + String sharedId = jsonRsp.getString("sharedId"); + + // In case of requesting the content only, Content-Disposition should be present to force browsers to download the file + rsp = sendRequest(new GetRequest(SHARE_CONTENT_URL.replace("{shared_id}", sharedId)), 200, USER_TWO); + assertNotNull("The response should contain a Content-Disposition entry in the header", rsp.getHeader("Content-Disposition")); + + // In case of requesting the thumbnail, Content-Disposition should not be present + rsp = sendRequest(new GetRequest(SHARE_CONTENT_THUMBNAIL_URL.replace("{shared_id}", sharedId).replace("{thumbnailname}", "doclib")), 200, USER_TWO); + assertNull("The response should not contain a Content-Disposition entry in the header", rsp.getHeader("Content-Disposition")); + } private void createUser(String userName) {