简体   繁体   English

使用Javascript和Java验证图像上传

[英]Validating Image Uploads With Javascript & Java

The site I am designing allows users to upload images ( PNG , JPEG , or GIF ) to a servlet within my backend. 我正在设计的网站允许用户将图像( PNGJPEGGIF )上传到我后端的servlet中。 This is what I've accomplished so far in terms of security... 到目前为止,这是我在安全性方面所完成的...

  1. Validate the image client side by checking the files extension. 通过检查文件扩展名来验证图像客户端。 If the extension is valid send it to the servlet for backend validation. 如果扩展有效,则将其发送到servlet进行后端验证。
  2. Validate the mime type of the image and make sure it is either image/jpeg , image/gif , or image/png . 验证图像的MIME类型,并确保它是image/jpegimage/gifimage/png
  3. Read the first 10 bytes of the image, convert them to hex, and validate that the hex matches the magic numbers of either a PNG , JPEG or GIF . 读取图像的前10个字节,将其转换为十六进制,并验证十六进制是否与PNGJPEGGIF幻数匹配。 Here's an example of the magic numbers I get when a user uploads a PNG - 89 50 4E 47 0D 0A 1A 0A . 这是用户上载PNG时获得的幻数的示例89 50 4E 47 0D 0A 1A 0A

So mime and magic number validation on the server side, and extension validation on the client side. 因此,在服务器端进行mime和magic数字验证,在客户端进行扩展验证。 Everything works great but I have two quick questions... 一切都很好,但是我有两个快速问题。

  1. Is there any purpose to send the file name to the servlet to check the extension server side since I'm already checking the mime and magic? 因为我已经检查过mime和magic,是否有将文件名发送到servlet来检查扩展服务器端的目的?
  2. What else should I do in terms of security, and what would you change about my current approach? 在安全性方面,我还应该做些什么?您对我目前的方法有何改变?

And please don't say "I don't think it's really necessary" to any security steps because my number one goal here is to learn. 而且,请不要对任何安全步骤说“我认为这不是真正必要的”,因为我的首要目标是学习。 So even if there is only a .001% chance that my site could be at risk, I'd still like to learn the best way to protect myself. 因此,即使只有0.001%的机会使我的网站受到威胁,我仍然想学习保护自己的最佳方法。 Thank you. 谢谢。

Validate the image client side by checking the files extension. 通过检查文件扩展名来验证图像客户端。 If the extension is valid send it to the servlet for backend validation. 如果扩展有效,则将其发送到servlet进行后端验证。

This may fail for non-Windows users, whose filetypes are not necessarily determined by an extension on the filename. 对于非Windows用户,这可能会失败,其文件类型不一定由文件名上的扩展名确定。

It can be useful to add a JS warning to say "this file doesn't end in .png/.gif/.jpeg/.jpg - are you sure it is an image?", but it's generally not a good idea to disallow an upload based on extension. 添加JS警告说“此文件未以.png / .gif / .jpeg / .jpg结尾-您确定它是图像吗?”可能会很有用,但是通常不允许将其禁用基于扩展名的上传。

Validate the mime type of the image and make sure it is either image/jpeg, image/gif, or image/png. 验证图像的MIME类型,并确保其为image / jpeg,image / gif或image / png。

Again there are some problems here. 再次有一些问题。 On Windows, the MIME type is retrieved from registry associations, which are variable and not always correct. 在Windows上,MIME类型是从注册表关联中检索的,这些关联是可变的,并不总是正确的。 For example IE commonly sends JPEGs as image/pjpeg , and Citrix users may find they get uploaded as image/x-citrix-pjpeg . 例如,IE通常将JPEG作为image/pjpeg发送,而Citrix用户可能会发现他们以image/x-citrix-pjpeg

Since the media type is typically unused by upload scripts, there's little point reading/checking it. 由于上载脚本通常不使用媒体类型,因此几乎没有必要读取/检查它。 For the types here, I'd say your best bet would be to ignore the filename and MIME type; 对于这里的类型,我想说最好的选择是忽略文件名和MIME类型。 use only the magic number sniffing to determine format. 仅使用幻数嗅探来确定格式。

What else should I do in terms of security 在安全性方面我还应该做什么

1) Be careful what name you use to store the file - taking the user's submitted filename verbatim is dangerous due to directory traversal, special filenames and extensions ( .htaccess , .jsp etc), and unreliable just because file naming rules can be complicated cross-platform. 1)注意使用什么名称存储文件-由于目录遍历,特殊的文件名和扩展名( .htaccess.jsp等),逐字记录用户提交的文件名是危险的,并且仅由于文件命名规则可能会很复杂而变得不可靠-平台。

If you want to use the supplied name on the local filesystem at all it should be basenamed, slugified (replacing all but a whitelist of simple characters), length-limited, and the extension replaced/added from the detected filetype. 如果要在本地文件系统上完全使用提供的名称,则应使用基本名称,分段(替换除简单字符的白名单以外的所有内容),长度限制以及从检测到的文件类型替换/添加的扩展名。

Better is to store the file with a completely generated name (eg 17264.dat for the file related to item with primary key 17264 in the database); 更好的做法是使用完整生成的名称存储文件(例如,与数据库中主键为17264的项目相关的文件的名称为17264.dat ); if you need to serve it up to browsers with a pretty filename you can use rewrites on the front-end web server, or a file-serving servlet, to make it visible as /images/17264/some_name.png . 如果需要使用漂亮的文件名将其提供给浏览器,则可以在前端Web服务器或文件服务servlet上使用重写,以使其显示为/images/17264/some_name.png

2) Just because it has image magic numbers doesn't mean it's necessarily an image, or that even if it is a valid image, it doesn't have some other content in a different form at the same time (a 'chameleon' file). 2)仅仅因为它具有图像魔术数字并不意味着它一定是图像,或者即使它有效图像,也不会同时具有其他形式的其他内容(“变色龙”文件)。

For example, HTML-like content in a binary file can fool the dodgy MIME-sniffing in older versions of IE into treating it as HTML. 例如,二进制文件中类似HTML的内容可能使IE较旧版本中晦涩的MIME嗅探愚弄成HTML。 Similarly Flash could be tricked into loading a <crossdomain> policy set out of XML inside an image, and Java could load applets that were also GIFs. 类似地,Flash可能会被诱骗在图像内部加载XML设置的<crossdomain>策略,而Java可能加载也是GIF的小程序。

One way of making this much harder is to load the image using a server-side graphics library, and then re-save it, causing a round of recompression which will generally garble any parsable content in the file. 使其变得更困难的一种方法是使用服务器端图形库加载图像,然后重新保存它,从而引起一轮重新压缩,这通常会使文件中的所有可分析内容混乱。 The problem with this is for lossy compression like JPEG, where recompressing results in a loss of visual quality. 这样做的问题是像JPEG这样的有损压缩,其中重新压缩会导致视觉质量下降。

The ultimate solution is usually to give up and serve the image from a completely different hostname to the main site. 最终的解决方案通常是放弃与主机名完全不同的主机名并将其提供给主站点。 Then if the attacker manages to get some XSS content into the file, it doesn't matter as there's nothing on the site it's living in to compromise, only other static images. 然后,如果攻击者设法将一些XSS内容添加到文件中,则没关系,因为该网站上没有任何东西可以入侵,只有其他静态映像会受到影响。

3) If you do load the image server-side, for (2) or other reasons, ensure that the image size - both file size and width/height size - is reasonable before attempting to load it. 3)如果出于(2)或其他原因确实加载了图像,请在尝试加载之前确保图像大小-文件大小和宽度/高度大小均合理。 Otherwise you can be hit by decompression bombs filling up your memory and causing denial of service. 否则,您可能会被减压炸弹击中,这些炸弹会填满您的记忆并导致拒绝服务。

Also if you do this make sure to keep your image library/language (eg Java Graphics2D ) up to date. 另外,如果这样做,请确保图像库/语言(例如Java Graphics2D )保持最新。 There have been image-handling vulnerabilities in these languages before. 以前,这些语言中存在图像处理漏洞。

I love your question! 我爱你的问题! Steps one, two, and three are excellent for security. 步骤1,步骤2和步骤3非常适合安全性。 Good work! 干得好!

1) Is there any purpose to send the file name to the servlet to check the extension server side since I'm already checking the mime and magic? 1)因为我已经检查过mime和magic,是否有将文件名发送到servlet来检查扩展服务器端的目的?

No, not really. 不,不是。 The extension is a meaningless token that only has value when attempting to interpret the data contained in the file. 扩展名是无意义的令牌,仅在尝试解释文件中包含的数据时才有价值。 Your client side validation can be easily bypassed by even novice attackers, but I would still do it because not only can you save yourself some bandwidth by weeding out the least competent script kiddies, but you can also provide quicker error messages to users making an honest mistake. 即使是新手攻击者也可以轻松绕过客户端验证,但我仍然会这样做,因为您不仅可以通过淘汰能力最差的脚本小子来节省一些带宽,而且还可以向用户诚实地提供更快的错误消息错误。 You are checking for the "magic numbers" server side, which is the right way to do it. 您正在检查服务器端的“魔术数字”,这是正确的方法。 It doesn't mean there isn't evil code, but it certainly makes embedding the evil code harder. 这并不意味着没有邪恶代码,但是它无疑会使嵌入邪恶代码更加困难。 You'll never stop the elite forever, but you can slow them down and stop everyone else. 您永远不会永远停止精英,但是您可以放慢他们的脚步并停止其他所有人。

2) What else should I do in terms of security, and what would you change about my current approach? 2)就安全性而言,我还应该做些什么?您对我目前的方法有何改变?

Your current approach is good. 您当前的方法是好的。 I would consider adding file size restriction enforced both client-side and server-side. 我会考虑在客户端和服务器端都添加文件大小限制。 The client-side can be easily defeated, but again it saves you bandwidth and costs the attacker time. 客户端很容易被击败,但是它又节省了带宽并浪费了攻击者的时间。 Images should not reasonably be more than a couple MB unless you're building a photo editing app or something similar. 除非您要构建照片编辑应用程序或类似工具,否则图像的大小不应超过几MB。

Something else that I would be careful about, is what applications you process the photo with. 我还要注意的另一件事是,您使用什么应用程序处理照片。 Some photo programs have vulnerabilities in them that can allow an attacker to get a remote shell to your server if the photo is opened with that application. 某些照片程序中包含漏洞,如果使用该应用程序打开照片,则攻击者可以利用该漏洞将远程外壳获取到您的服务器。 This is rare but it does happen (this falls into your .001% chance). 这种情况很少见,但确实会发生(这会降低您的.001%机率)。 Because of this be careful with any code that you write to process the photos, and any applications you let open it. 因此,请注意编写用于处理照片的任何代码以及允许打开的任何应用程序。 That is a deep subject. 这是一个很深的话题。 If you want to learn more about writing secure code, I highly recommend Secure Coding by Robert Seacord . 如果您想了解有关编写安全代码的更多信息,我强烈建议您使用Robert Seacord的“安全编码” I not only learned a lot about code security, but also writing less buggy code. 我不仅学到了很多关于代码安全性的知识,而且还编写了更少的错误代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM