浏览器提交表单 Html <input type file generates request that Ubuntu Tomcat Apache 服务器拒绝

Browser submit form Html <input type file generates request that Ubuntu Tomcat Apache server rejects

我在 Tomcat9 apache 的 Ubuntu 20.04.2 上有一个 Java servlet 应用 运行ning。 servlet 需要 运行 使用 https 传输。我正在使用自签名证书在 Tomcat9 上启用 https/TLS。

servlet 尝试使用包含 html 输入标签的文件类型文件的表单上传文件。

在我的局域网中,tomcat 在 ip4 地址为 10.0.0.200 的机器上 运行ning。

如果我从同一台机器上的浏览器访问 servlet(例如 ip4 地址 10.0.0.200),因为 tomcat 服务器是 运行ning(例如 url https://10.0.0.200:8443/MyServlet), 它工作正常,文件已上传。

如果我从 tomcat 服务器所在 运行ning 以外的机器上的浏览器访问 servlet(例如来自 ip4 地址为 10.0.0.30 的机器),post来自表单提交按钮的请求 永远不会到达 servlet(示例 url https://10.0.0.200:8443/MyServlet 来自机器 10.0.0.30)。

浏览器 (FireFox) 报告: “连接到 10.0.0.200:8443 时发生错误。 PR_CONNECT_RESET_ERROR 您尝试查看的页面无法显示,因为无法验证接收到的数据的真实性。 请联系网站所有者,告知他们此问题。"

为什么这不起作用?

这是记录形式 post:


    POST /TempClientServlet/TempResult undefined
Host: 24.63.181.39:8443
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------3698066889622667783588722062
Content-Length: 970929
Origin: https://24.63.181.39:8443
Connection: keep-alive
Referer: https://24.63.181.39:8443/TempClientServlet/TempResult
Cookie: user_name=ccervo; textCookie=ccervo; JSESSIONID=401E418CE0A56EFB5A34784DE97DC996
Upgrade-Insecure-Requests: 1

这是 web.xml 文件:


    <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
  id="WebApp_ID" version="3.0">
  <display-name>TempClientServlet</display-name>
  <absolute-ordering />
  <welcome-file-list>
    <welcome-file>TempClient</welcome-file>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <session-config>
    <cookie-config>
      <http-only>true</http-only>
      <secure>true</secure>
    </cookie-config>
  </session-config>
       
 <security-constraint>
    <web-resource-collection>
        <web-resource-name>secured page</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>          
        <transport-guarantee>CONFIDENTIAL</transport-guarantee> 
    </user-data-constraint>
 </security-constraint>
</web-app>

这是 server.xml 文件:

    <?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
--><!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 -->
 <Server port="8081" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
  </GlobalNamingResources>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->
    <Connector connectionTimeout="20000"
     port="8080"
     protocol="HTTP/1.1"
     redirectPort="8443"
     />
     <!-- uncomment this to run servlets secure ie. https -->
      <Connector SSLEnabled="true"
       clientAuth="false"
        keystoreFile="/home/foobar/.keystore"
        keystorePass="changeit"
        maxThreads="200"
        port="8443"
        protocol="org.apache.coyote.http11.Http11NioProtocol"
        scheme="https"
        secure="true" 
        sslProtocol="TLS"
        />
    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <!--
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    -->
    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine defaultHost="localhost" name="Catalina">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
    
            <!-- SingleSignOn valve, share authentication between web applications
                 Documentation at: /docs/config/valve.html -->
            <!--
            <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
            -->
    
            <!-- Access log processes all example.
                 Documentation at: /docs/config/valve.html
                 Note: The pattern used is equivalent to using pattern="common" -->
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log" suffix=".txt"/>
    
          <Context docBase="TempClientServlet" path="/TempClientServlet" reloadable="true" source="org.eclipse.jst.jee.server:TempClientServlet"/>
          <Context path="/TempClientServlet/images" docBase="/var/opt/TempClientServletimages" crossContext="true"/>
          <Context docBase="Temp_Service" path="/Temp_Service" reloadable="true" source="org.eclipse.jst.jee.server:Temp_Service"/>
          <Context docBase="Temp_ServiceClient" path="/Temp_ServiceClient" reloadable="true" source="org.eclipse.jst.jee.server:Temp_ServiceClient"/>
      </Host>
    </Engine>
  </Service>
</Server>

没有 100% 的答案,但足够接近我自己到达那里。

获得答案的步骤:

  1. 我写了一个 tunnel/proxy 将用户代理(浏览器)连接到 tomcat 运行ning 我的 servlet 并记录两者之间的所有 TCP 流量。
  2. 向我的 servlet 添加了调试代码以消除会话 ID 和 cookie 的使用。 我会在找到错误后修复它。
  3. 已在 Eclipse 中配置 server.xml 在端口 8080 和 8443 上创建连接器 是 HTTP 连接器。
  4. 已为 servlet 配置 web.xml 以允许 http 访问。 注意事项和建议: 如果您尝试调试安全的 servlet,则需要避免许多陷阱。
  5. 如果您尝试从 Eclipse 调试 servlet,请注意这些陷阱。 a) Eclipse 调试器 运行s Tomcat 在与 Tomcat 不同的环境中 服务 运行。具体来说,Tomcat 服务是沙盒化的。 Tomcat 运行 通过 Eclipse 调试器不是。这可能会导致 servlet 表现异常 在调试器中 运行 和通过服务 运行 时不同。 b) Linux、Ubuntu、运行s Tomcat 作为用户名“tomcat”下的服务。蚀 debugger on Ubuntu 运行s Tomcat 用户名下启动 蚀。这可能会导致 servlet 表现异常 在调试器中 运行 和通过服务 运行 时不同。
  6. 如果您在调试时在 http 和 https 之间切换,请注意这些陷阱。 a) 如果您的 servlet 使用 J2EE 会话 and/or cookie,请注意 servlet 容器的行为取决于适用的 web.xml(应用程序的 and/or 服务器)中的设置。如果 web.xml 指定了带有传输保证机密的安全约束,则 servlet 容器 在所有 cookie 上设置“secure”和“http/only”属性(包括 jsession cookie)。如果没有约束,servlet 容器不会在 cookie 上设置“secure”and/or“http/only”。 http 规范说用户代理、浏览器将 如果传输是安全的 (https/tls),则仅 pass/return 安全 cookie。这可能 导致 servlet 丢失会话,并且在使用 http 访问与使用访问时表现截然不同 https。 b) 如果您在与您的计算机相同的物理机器上使用用户代理进行调试 servlet 服务器,http 规范说用户代理可以通过安全 cookie,即使传输不是 https 且不安全。这可能 使用 via 访问时导致 servlet 的行为大不相同 用户代理 运行ning 在与 servlet 服务器相同的机器上。 因此,在了解所有这些陷阱之后,我现在让我的 servlet 运行 通过 http 隧道运行。我可以观察所有的 TCP 流量。 运行 不同机器上的用户代理和 servlet 服务器; 运行在 Eclipse 调试器中运行; 运行在 HTTP 中宁; 运行 已删除 cookie 和会话的使用;我的 servlet 运行宁 完全没有问题(只是通过 http 隧道记录的结果变慢了)。我现在需要恢复 server.xml 和 web.xml 才能使用 https;恢复会话和 cookie 的使用,并确定该配置引发错误的原因。这将是缓慢而痛苦的,但我想我可以坚持下去。

servlet 没有损坏。它运行正常。该行为是围绕 HTML 类型为“文件”的“

"PR_CONNECT_RESET_ERROR 由于无法验证接收到的数据的真实性,无法显示您尝试查看的页面。"

所以解决方案是:

  1. 注册域并购买经过身份验证的证书
  2. 运行 使用 http
  3. 的 servlet 不安全
  4. 接受问题