mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2026-01-02 14:44:45 +03:00
Java test WebApp
This commit is contained in:
committed by
Felipe Zimmerle
parent
a6c1627987
commit
b9080aad18
71
java/ModSecurityTestApp/build.xml
Normal file
71
java/ModSecurityTestApp/build.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="ModSecurityTestApp" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project ModSecurityTestApp.</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-dist: called before archive building
|
||||
-post-dist: called after archive building
|
||||
-post-clean: called after cleaning build products
|
||||
-pre-run-deploy: called before deploying
|
||||
-post-run-deploy: called after deploying
|
||||
|
||||
Example of pluging an obfuscator after the compilation could look like
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Other way how to customize the build is by overriding existing main targets.
|
||||
The target of interest are:
|
||||
|
||||
init-macrodef-javac: defines macro for javac compilation
|
||||
init-macrodef-junit: defines macro for junit execution
|
||||
init-macrodef-debug: defines macro for class debugging
|
||||
do-dist: archive building
|
||||
run: execution of project
|
||||
javadoc-build: javadoc generation
|
||||
|
||||
Example of overriding the target for project execution could look like
|
||||
|
||||
<target name="run" depends="<PROJNAME>-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that overridden target depends on jar target and not only on
|
||||
compile target as regular run target does. Again, for list of available
|
||||
properties which you can use check the target you are overriding in
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
</project>
|
||||
2
java/ModSecurityTestApp/src/conf/MANIFEST.MF
Normal file
2
java/ModSecurityTestApp/src/conf/MANIFEST.MF
Normal file
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.modsecurity;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public final class ModSecurity {
|
||||
|
||||
public static final int DONE = -2;
|
||||
public static final int DECLINED = -1;
|
||||
public static final int OK = 0;
|
||||
|
||||
//From build/classes: >"c:\Program Files\Java\jdk1.7.0_05\bin\javah.exe" -classpath c:\work\apache-tomcat-7.0.39\lib\servlet-api.jar;. org.modsecurity.ModSecurity
|
||||
|
||||
private FilterConfig filterConfig;
|
||||
private String confFilename;
|
||||
private long confTime;
|
||||
private final static String pathToLib = "c:\\work\\mod_security\\java\\Debug\\";
|
||||
static {
|
||||
try {
|
||||
//TODO: bad practice, native libraries should be loaded in server's classloader
|
||||
System.load("c:\\work\\mod_security\\java\\libs\\zlib1.dll");
|
||||
System.load("c:\\work\\mod_security\\java\\libs\\libxml2.dll");
|
||||
System.load("c:\\work\\mod_security\\java\\libs\\pcre.dll");
|
||||
System.load("c:\\work\\mod_security\\java\\libs\\libapr-1.dll");
|
||||
System.load("c:\\work\\mod_security\\java\\libs\\libapriconv-1.dll");
|
||||
System.load("c:\\work\\mod_security\\java\\libs\\libaprutil-1.dll");
|
||||
System.load("c:\\work\\mod_security\\java\\Debug\\ModSecurityJNI.dll");
|
||||
} catch (UnsatisfiedLinkError err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
//java.lang.reflect.Field loadedLibraries = ClassLoader.class.getDeclaredField("loadedLibraryNames");
|
||||
//loadedLibraries.setAccessible(true);
|
||||
//final Vector<String> libraries = (Vector<String>) loadedLibraries.get(ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
|
||||
public ModSecurity(FilterConfig fc, String confFile) throws ServletException {
|
||||
this.filterConfig = fc;
|
||||
this.confFilename = confFile;
|
||||
confTime = new File(confFilename).lastModified();
|
||||
|
||||
this.initialize();
|
||||
filterConfig.getServletContext().log("ModSecurity started.");
|
||||
}
|
||||
|
||||
private native int initialize();
|
||||
|
||||
public native int destroy();
|
||||
|
||||
public native int onRequest(String config, ServletRequest request, HttpServletRequest httprequest, String requestID, boolean reloadConfig);
|
||||
|
||||
public native int onResponse(ServletResponse response, HttpServletResponse htttpResponse, String requestID);
|
||||
|
||||
public static String[][] getHttpRequestHeaders(HttpServletRequest req) {
|
||||
ArrayList<String> aList = Collections.list(req.getHeaderNames());
|
||||
String[][] result = new String[aList.size()][2];
|
||||
|
||||
for (int i = 0; i < aList.size(); i++) {
|
||||
result[i][0] = aList.get(i);
|
||||
result[i][1] = req.getHeader(aList.get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String[][] getHttpResponseHeaders(HttpServletResponse resp) {
|
||||
|
||||
Collection<String> headerNames = resp.getHeaderNames();
|
||||
String[][] result = new String[headerNames.size()][2];
|
||||
|
||||
int i = 0;
|
||||
for (String headerName : headerNames) {
|
||||
result[i][0] = headerName;
|
||||
result[i][1] = resp.getHeader(headerName);
|
||||
i++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isIPv6(String addr) {
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getByName(addr);
|
||||
|
||||
if (inetAddress instanceof Inet6Address) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (UnknownHostException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void log(int level, String msg) {
|
||||
//if (level == 1) {
|
||||
filterConfig.getServletContext().log(msg);
|
||||
//}
|
||||
}
|
||||
|
||||
public boolean checkModifiedConfig() {
|
||||
if (new File(confFilename).lastModified() > confTime) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getAppPath() {
|
||||
return filterConfig.getServletContext().getContextPath();
|
||||
}
|
||||
|
||||
public String getConfFilename() {
|
||||
return this.confFilename;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.modsecurity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* Docs: http://docs.oracle.com/javaee/6/tutorial/doc/bnagb.html
|
||||
*/
|
||||
public class ModSecurityFilter implements Filter {
|
||||
|
||||
ModSecurity modsecurity;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig fc) throws ServletException {
|
||||
String confFilename = fc.getInitParameter("conf");
|
||||
if (confFilename == null) {
|
||||
throw new ServletException("ModSecurity: parameter 'conf' not available in web.xml");
|
||||
} else {
|
||||
confFilename = fc.getServletContext().getRealPath(confFilename);
|
||||
}
|
||||
|
||||
modsecurity = new ModSecurity(fc, confFilename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc) throws IOException, ServletException {
|
||||
HttpServletRequest httpReq = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResp = (HttpServletResponse) response;
|
||||
|
||||
String requestID = UUID.randomUUID().toString();
|
||||
|
||||
int status = modsecurity.onRequest(modsecurity.getConfFilename(), request, httpReq, requestID, modsecurity.checkModifiedConfig());
|
||||
|
||||
if (status != ModSecurity.DECLINED) {
|
||||
return;
|
||||
}
|
||||
|
||||
fc.doFilter(request, response);
|
||||
|
||||
status = modsecurity.onResponse(response, httpResp, requestID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (modsecurity != null) {
|
||||
modsecurity.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
java/ModSecurityTestApp/web/META-INF/context.xml
Normal file
2
java/ModSecurityTestApp/web/META-INF/context.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Context antiJARLocking="true" path="/ModSecurityTestApp"/>
|
||||
12
java/ModSecurityTestApp/web/Post.jsp
Normal file
12
java/ModSecurityTestApp/web/Post.jsp
Normal file
@@ -0,0 +1,12 @@
|
||||
<%@page contentType="text/html" pageEncoding="UTF-8"%>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>JSP Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<p><%= request.getParameter( "data" ) %></p>
|
||||
</body>
|
||||
</html>
|
||||
214
java/ModSecurityTestApp/web/WEB-INF/modsecurity.conf
Normal file
214
java/ModSecurityTestApp/web/WEB-INF/modsecurity.conf
Normal file
@@ -0,0 +1,214 @@
|
||||
# based on modsecurity.conf-recommended
|
||||
# -- Rule engine initialization ----------------------------------------------
|
||||
|
||||
# Enable ModSecurity, attaching it to every transaction. Use detection
|
||||
# only to start with, because that minimises the chances of post-installation
|
||||
# disruption.
|
||||
#
|
||||
SecRuleEngine On
|
||||
|
||||
|
||||
# -- Request body handling ---------------------------------------------------
|
||||
|
||||
# Allow ModSecurity to access request bodies. If you don't, ModSecurity
|
||||
# won't be able to see any POST parameters, which opens a large security
|
||||
# hole for attackers to exploit.
|
||||
#
|
||||
SecRequestBodyAccess On
|
||||
|
||||
|
||||
# Enable XML request body parser.
|
||||
# Initiate XML Processor in case of xml content-type
|
||||
#
|
||||
SecRule REQUEST_HEADERS:Content-Type "text/xml" \
|
||||
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
|
||||
|
||||
|
||||
# Maximum request body size we will accept for buffering. If you support
|
||||
# file uploads then the value given on the first line has to be as large
|
||||
# as the largest file you are willing to accept. The second value refers
|
||||
# to the size of data, with files excluded. You want to keep that value as
|
||||
# low as practical.
|
||||
#
|
||||
SecRequestBodyLimit 13107200
|
||||
SecRequestBodyNoFilesLimit 131072
|
||||
|
||||
# Store up to 128 KB of request body data in memory. When the multipart
|
||||
# parser reachers this limit, it will start using your hard disk for
|
||||
# storage. That is slow, but unavoidable.
|
||||
#
|
||||
SecRequestBodyInMemoryLimit 131072
|
||||
|
||||
# What do do if the request body size is above our configured limit.
|
||||
# Keep in mind that this setting will automatically be set to ProcessPartial
|
||||
# when SecRuleEngine is set to DetectionOnly mode in order to minimize
|
||||
# disruptions when initially deploying ModSecurity.
|
||||
#
|
||||
SecRequestBodyLimitAction Reject
|
||||
|
||||
# Verify that we've correctly processed the request body.
|
||||
# As a rule of thumb, when failing to process a request body
|
||||
# you should reject the request (when deployed in blocking mode)
|
||||
# or log a high-severity alert (when deployed in detection-only mode).
|
||||
#
|
||||
SecRule REQBODY_ERROR "!@eq 0" \
|
||||
"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
|
||||
|
||||
# By default be strict with what we accept in the multipart/form-data
|
||||
# request body. If the rule below proves to be too strict for your
|
||||
# environment consider changing it to detection-only. You are encouraged
|
||||
# _not_ to remove it altogether.
|
||||
#
|
||||
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
|
||||
"id:'200002',phase:2,t:none,log,deny,status:44, \
|
||||
msg:'Multipart request body failed strict validation: \
|
||||
PE %{REQBODY_PROCESSOR_ERROR}, \
|
||||
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
|
||||
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
|
||||
DB %{MULTIPART_DATA_BEFORE}, \
|
||||
DA %{MULTIPART_DATA_AFTER}, \
|
||||
HF %{MULTIPART_HEADER_FOLDING}, \
|
||||
LF %{MULTIPART_LF_LINE}, \
|
||||
SM %{MULTIPART_MISSING_SEMICOLON}, \
|
||||
IQ %{MULTIPART_INVALID_QUOTING}, \
|
||||
IP %{MULTIPART_INVALID_PART}, \
|
||||
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
|
||||
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
|
||||
|
||||
# Did we see anything that might be a boundary?
|
||||
#
|
||||
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
|
||||
"id:'200003',phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'"
|
||||
|
||||
# PCRE Tuning
|
||||
# We want to avoid a potential RegEx DoS condition
|
||||
#
|
||||
SecPcreMatchLimit 1000
|
||||
SecPcreMatchLimitRecursion 1000
|
||||
|
||||
# Some internal errors will set flags in TX and we will need to look for these.
|
||||
# All of these are prefixed with "MSC_". The following flags currently exist:
|
||||
#
|
||||
# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
|
||||
#
|
||||
SecRule TX:/^MSC_/ "!@streq 0" \
|
||||
"id:'200004',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
|
||||
|
||||
|
||||
# -- Response body handling --------------------------------------------------
|
||||
|
||||
# Allow ModSecurity to access response bodies.
|
||||
# You should have this directive enabled in order to identify errors
|
||||
# and data leakage issues.
|
||||
#
|
||||
# Do keep in mind that enabling this directive does increases both
|
||||
# memory consumption and response latency.
|
||||
#
|
||||
#SecResponseBodyAccess On
|
||||
|
||||
# Which response MIME types do you want to inspect? You should adjust the
|
||||
# configuration below to catch documents but avoid static files
|
||||
# (e.g., images and archives).
|
||||
#
|
||||
SecResponseBodyMimeType text/plain text/html text/xml
|
||||
|
||||
# Buffer response bodies of up to 512 KB in length.
|
||||
SecResponseBodyLimit 524288
|
||||
|
||||
# What happens when we encounter a response body larger than the configured
|
||||
# limit? By default, we process what we have and let the rest through.
|
||||
# That's somewhat less secure, but does not break any legitimate pages.
|
||||
#
|
||||
SecResponseBodyLimitAction ProcessPartial
|
||||
|
||||
|
||||
# -- Filesystem configuration ------------------------------------------------
|
||||
|
||||
# The location where ModSecurity stores temporary files (for example, when
|
||||
# it needs to handle a file upload that is larger than the configured limit).
|
||||
#
|
||||
# This default setting is chosen due to all systems have /tmp available however,
|
||||
# this is less than ideal. It is recommended that you specify a location that's private.
|
||||
#
|
||||
SecTmpDir c:\inetpub\temp\
|
||||
|
||||
# The location where ModSecurity will keep its persistent data. This default setting
|
||||
# is chosen due to all systems have /tmp available however, it
|
||||
# too should be updated to a place that other users can't access.
|
||||
#
|
||||
SecDataDir c:\inetpub\temp\
|
||||
|
||||
|
||||
# -- File uploads handling configuration -------------------------------------
|
||||
|
||||
# The location where ModSecurity stores intercepted uploaded files. This
|
||||
# location must be private to ModSecurity. You don't want other users on
|
||||
# the server to access the files, do you?
|
||||
#
|
||||
#SecUploadDir /opt/modsecurity/var/upload/
|
||||
|
||||
# By default, only keep the files that were determined to be unusual
|
||||
# in some way (by an external inspection script). For this to work you
|
||||
# will also need at least one file inspection rule.
|
||||
#
|
||||
#SecUploadKeepFiles RelevantOnly
|
||||
|
||||
# Uploaded files are by default created with permissions that do not allow
|
||||
# any other user to access them. You may need to relax that if you want to
|
||||
# interface ModSecurity to an external program (e.g., an anti-virus).
|
||||
#
|
||||
#SecUploadFileMode 0600
|
||||
|
||||
|
||||
# -- Debug log configuration -------------------------------------------------
|
||||
|
||||
# The default debug log configuration is to duplicate the error, warning
|
||||
# and notice messages from the error log.
|
||||
#
|
||||
#SecDebugLog /opt/modsecurity/var/log/debug.log
|
||||
#SecDebugLogLevel 3
|
||||
|
||||
|
||||
# -- Audit log configuration -------------------------------------------------
|
||||
|
||||
# Log the transactions that are marked by a rule, as well as those that
|
||||
# trigger a server error (determined by a 5xx or 4xx, excluding 404,
|
||||
# level response status codes).
|
||||
#
|
||||
#SecAuditEngine RelevantOnly
|
||||
#SecAuditLogRelevantStatus "^(?:5|4(?!04))"
|
||||
|
||||
# Log everything we know about a transaction.
|
||||
#SecAuditLogParts ABIJDEFHZ
|
||||
|
||||
# Use a single file for logging. This is much easier to look at, but
|
||||
# assumes that you will use the audit log only ocassionally.
|
||||
#
|
||||
#SecAuditLogType Serial
|
||||
#SecAuditLog c:\inetpub\log\modsec_audit.log
|
||||
|
||||
# Specify the path for concurrent audit logging.
|
||||
#SecAuditLogStorageDir c:\inetpub\log\
|
||||
|
||||
|
||||
# -- Miscellaneous -----------------------------------------------------------
|
||||
|
||||
# Use the most commonly used application/x-www-form-urlencoded parameter
|
||||
# separator. There's probably only one application somewhere that uses
|
||||
# something else so don't expect to change this value.
|
||||
#
|
||||
SecArgumentSeparator &
|
||||
|
||||
# Settle on version 0 (zero) cookies, as that is what most applications
|
||||
# use. Using an incorrect cookie version may open your installation to
|
||||
# evasion attacks (against the rules that examine named cookies).
|
||||
#
|
||||
SecCookieFormat 0
|
||||
|
||||
# Specify your Unicode Code Point.
|
||||
# This mapping is used by the t:urlDecodeUni transformation function
|
||||
# to properly map encoded data to your language. Properly setting
|
||||
# these directives helps to reduce false positives and negatives.
|
||||
#
|
||||
#SecUnicodeCodePage 20127
|
||||
#SecUnicodeMapFile unicode.mapping
|
||||
22
java/ModSecurityTestApp/web/WEB-INF/web.xml
Normal file
22
java/ModSecurityTestApp/web/WEB-INF/web.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
|
||||
|
||||
<web-app>
|
||||
|
||||
<display-name>ModSecurity for Java</display-name>
|
||||
|
||||
<filter>
|
||||
<filter-name>ModSecurityFilter</filter-name>
|
||||
<filter-class>org.modsecurity.ModSecurityFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>conf</param-name>
|
||||
<param-value>/WEB-INF/modsecurity.conf</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>ModSecurityFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
</web-app>
|
||||
14
java/ModSecurityTestApp/web/index.jsp
Normal file
14
java/ModSecurityTestApp/web/index.jsp
Normal file
@@ -0,0 +1,14 @@
|
||||
<%@page contentType="text/html" pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>JSP Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<form method="post" action="Post.jsp">
|
||||
Post Action: <input type="text" name="data" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user