diff --git a/Dockerfile b/Dockerfile
index 38c1310..3f5d504 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -81,7 +81,8 @@ RUN apk add --no-cache \
         flask_gravatar \
         flask_migrate \
         simplejson \
-        cryptography
+        cryptography \
+        netaddr
 
 # Copy the docs from the local tree. Explicitly remove any existing builds that
 # may be present
@@ -177,6 +178,7 @@ RUN ln -sf /usr/lib/libpq.so.5.12 /usr/lib/libpq.so.5
 
 # Copy the runner script
 COPY pkg/docker/run_pgadmin.py /pgadmin4
+COPY pkg/docker/gunicorn_config.py /pgadmin4
 COPY pkg/docker/entrypoint.sh /entrypoint.sh
 
 # Precompile and optimize python code to save time and space on startup
diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh
index 5a482c7..93d809f 100755
--- a/pkg/docker/entrypoint.sh
+++ b/pkg/docker/entrypoint.sh
@@ -58,7 +58,7 @@ TIMEOUT=$(cd /pgadmin4 && python -c 'import config; print(config.SESSION_EXPIRAT
 # Using --threads to have multi-threaded single-process worker
 
 if [ ! -z ${PGADMIN_ENABLE_TLS} ]; then
-    exec gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} --keyfile /certs/server.key --certfile /certs/server.cert run_pgadmin:app
+    exec gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app
 else
-    exec gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} run_pgadmin:app
+    exec gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} -c gunicorn_config.py run_pgadmin:app
 fi
diff --git a/pkg/docker/gunicorn_config.py b/pkg/docker/gunicorn_config.py
new file mode 100644
index 0000000..513c889
--- /dev/null
+++ b/pkg/docker/gunicorn_config.py
@@ -0,0 +1,2 @@
+import gunicorn
+gunicorn.SERVER_SOFTWARE = 'Python'
diff --git a/requirements.txt b/requirements.txt
index a5815a3..dbb0083 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -41,4 +41,5 @@ Flask-Security-Too>=3.0.0
 bcrypt<=3.1.7
 cryptography<=3.0
 sshtunnel>=0.1.5
+netaddr==0.8.0
 ldap3>=2.5.1
diff --git a/web/config.py b/web/config.py
index 702e73f..5d80a28 100644
--- a/web/config.py
+++ b/web/config.py
@@ -143,12 +143,53 @@ DEFAULT_SERVER = '127.0.0.1'
 # environment by the runtime
 DEFAULT_SERVER_PORT = 5050
 
+# This param is used to validate ALLOWED_HOSTS for the application
+# This will be used to avoid Host Header Injection attack
+# For how to set ALLOWED_HOSTS see netaddr library
+# For more details https://netaddr.readthedocs.io/en/latest/tutorial_03.html
+# e.g. ALLOWED_HOSTS = ['192.0.2.0/28', '::192.0.2.0/124']
+# ALLOWED_HOSTS = ['225.0.0.0/8', '226.0.0.0/7', '228.0.0.0/6']
+# ALLOWED_HOSTS = ['127.0.0.1', '192.168.0.1']
+# if ALLOWED_HOSTS= [] then it will accept all ips (and application will be
+# vulnerable to Host Header Injection attack)
+ALLOWED_HOSTS = []
+
+# This param is used to override the default web server information about
+# the web technology and the frameworks being used in the application
+# An attacker could use this information to fingerprint underlying operating
+# system and research known exploits for the specific version of
+# software in use
+WEB_SERVER = 'Python'
+
 # Enable X-Frame-Option protection.
 # Set to one of "SAMEORIGIN", "ALLOW-FROM origin" or "" to disable.
 # Note that "DENY" is NOT supported (and will be silently ignored).
 # See https://tools.ietf.org/html/rfc7034 for more info.
 X_FRAME_OPTIONS = "SAMEORIGIN"
 
+# The Content-Security-Policy header allows you to restrict how resources
+# such as JavaScript, CSS, or pretty much anything that the browser loads.
+# see https://content-security-policy.com/#source_list for more info
+# e.g. "default-src https: data: 'unsafe-inline' 'unsafe-eval';"
+CONTENT_SECURITY_POLICY = "default-src http: data: blob: 'unsafe-inline' " \
+                          "'unsafe-eval';"
+
+# The Strict-Transport-Security header tells the browser to convert all HTTP
+# requests to HTTPS, preventing man-in-the-middle (MITM) attacks.
+# e.g. 'max-age=31536000; includeSubDomains'
+STRICT_TRANSPORT_SECURITY = "max-age=31536000; includeSubDomains"
+
+# The X-Content-Type-Options header forces the browser to honor the response
+# content type instead of trying to detect it, which can be abused to
+# generate a cross-site scripting (XSS) attack.
+# e.g. nosniff
+X_CONTENT_TYPE_OPTIONS = "nosniff"
+
+# The browser will try to prevent reflected XSS attacks by not loading the
+# page if the request contains something that looks like JavaScript and the
+# response contains the same data. e.g. '1; mode=block'
+X_XSS_PROTECTION = "1; mode=block"
+
 # Hashing algorithm used for password storage
 SECURITY_PASSWORD_HASH = 'pbkdf2_sha512'
 
@@ -421,12 +462,14 @@ ON_DEMAND_RECORD_COUNT = 1000
 SHOW_GRAVATAR_IMAGE = True
 
 ##########################################################################
-# Set cookie path
+# Set cookie path and options
 ##########################################################################
 COOKIE_DEFAULT_PATH = '/'
 COOKIE_DEFAULT_DOMAIN = None
 SESSION_COOKIE_DOMAIN = None
 SESSION_COOKIE_SAMESITE = 'Lax'
+SESSION_COOKIE_SECURE = False
+SESSION_COOKIE_HTTPONLY = True
 
 #########################################################################
 # Skip storing session in files and cache for specific paths
diff --git a/web/pgadmin/__init__.py b/web/pgadmin/__init__.py
index 275f365..59cab62 100644
--- a/web/pgadmin/__init__.py
+++ b/web/pgadmin/__init__.py
@@ -12,6 +12,7 @@ such as setup of logging, dynamic loading of modules etc."""
 import logging
 import os
 import sys
+import re
 from types import MethodType
 from collections import defaultdict
 from importlib import import_module
@@ -19,11 +20,13 @@ from importlib import import_module
 from flask import Flask, abort, request, current_app, session, url_for
 from werkzeug.exceptions import HTTPException
 from flask_babelex import Babel, gettext
+from flask_babelex import gettext as _
 from flask_login import user_logged_in, user_logged_out
 from flask_mail import Mail
 from flask_paranoid import Paranoid
 from flask_security import Security, SQLAlchemyUserDatastore, current_user
 from flask_security.utils import login_user, logout_user
+from netaddr import IPSet
 from werkzeug.datastructures import ImmutableDict
 from werkzeug.local import LocalProxy
 from werkzeug.utils import find_modules
@@ -36,9 +39,10 @@ from pgadmin.utils.session import create_session_interface, pga_unauthorised
 from pgadmin.utils.versioned_template_loader import VersionedTemplateLoader
 from datetime import timedelta
 from pgadmin.setup import get_version, set_version
-from pgadmin.utils.ajax import internal_server_error
+from pgadmin.utils.ajax import internal_server_error, make_json_response
 from pgadmin.utils.csrf import pgCSRFProtect
 from pgadmin import authenticate
+from pgadmin.utils.security_headers import SecurityHeaders
 
 winreg = None
 if os.name == 'nt':
@@ -658,6 +662,36 @@ def create_app(app_name=None):
                 request.endpoint not in ('security.login', 'security.logout'):
             logout_user()
 
+    @app.before_request
+    def limit_host_addr():
+        """
+        This function validate the hosts from ALLOWED_HOSTS before allowing
+        HTTP request to avoid Host Header Injection attack
+        :return: None/JSON response with 403 HTTP status code
+        """
+        client_host = str(request.host).split(':')[0]
+        valid = True
+        allowed_hosts = config.ALLOWED_HOSTS
+
+        if len(allowed_hosts) != 0:
+            regex = re.compile(
+                r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:/\d{1,2}|)')
+            # Create separate list for ip addresses and host names
+            ip_set = list(filter(lambda ip: regex.match(ip), allowed_hosts))
+            host_set = list(filter(lambda ip: not regex.match(ip),
+                                   allowed_hosts))
+            is_ip = regex.match(client_host)
+            if is_ip:
+                valid = IPSet(ip_set).__contains__(client_host)
+            else:
+                valid = host_set.__contains__(client_host)
+
+        if not valid:
+            return make_json_response(
+                status=403, success=0,
+                errormsg=_("403 FORBIDDEN")
+            )
+
     @app.after_request
     def after_request(response):
         if 'key' in request.args:
@@ -667,13 +701,12 @@ def create_app(app_name=None):
                 domain['domain'] = config.COOKIE_DEFAULT_DOMAIN
             response.set_cookie('PGADMIN_INT_KEY', value=request.args['key'],
                                 path=config.COOKIE_DEFAULT_PATH,
+                                secure=config.SESSION_COOKIE_SECURE,
+                                httponly=config.SESSION_COOKIE_HTTPONLY,
+                                samesite=config.SESSION_COOKIE_SAMESITE,
                                 **domain)
 
-        # X-Frame-Options for security
-        if config.X_FRAME_OPTIONS != "" and \
-                config.X_FRAME_OPTIONS.lower() != "deny":
-            response.headers["X-Frame-Options"] = config.X_FRAME_OPTIONS
-
+        SecurityHeaders.set_response_headers(response)
         return response
 
     ##########################################################################
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 5375e94..6ed138b 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -697,6 +697,9 @@ def index():
 
     response.set_cookie("PGADMIN_LANGUAGE", value=language,
                         path=config.COOKIE_DEFAULT_PATH,
+                        secure=config.SESSION_COOKIE_SECURE,
+                        httponly=config.SESSION_COOKIE_HTTPONLY,
+                        samesite=config.SESSION_COOKIE_SAMESITE,
                         **domain)
 
     return response
diff --git a/web/pgadmin/preferences/__init__.py b/web/pgadmin/preferences/__init__.py
index db11d1d..d05c1c8 100644
--- a/web/pgadmin/preferences/__init__.py
+++ b/web/pgadmin/preferences/__init__.py
@@ -232,6 +232,9 @@ def save(pid):
     setattr(session, 'PGADMIN_LANGUAGE', language)
     response.set_cookie("PGADMIN_LANGUAGE", value=language,
                         path=config.COOKIE_DEFAULT_PATH,
+                        secure=config.SESSION_COOKIE_SECURE,
+                        httponly=config.SESSION_COOKIE_HTTPONLY,
+                        samesite=config.SESSION_COOKIE_SAMESITE,
                         **domain)
 
     return response
diff --git a/web/pgadmin/utils/security_headers.py b/web/pgadmin/utils/security_headers.py
new file mode 100644
index 0000000..c3cf31c
--- /dev/null
+++ b/web/pgadmin/utils/security_headers.py
@@ -0,0 +1,36 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+#########################################################################
+
+import config
+
+
+class SecurityHeaders:
+
+    @staticmethod
+    def set_response_headers(response):
+        """set response security headers"""
+
+        params_dict = {
+            'CONTENT_SECURITY_POLICY': 'Content-Security-Policy',
+            'STRICT_TRANSPORT_SECURITY': 'Strict-Transport-Security',
+            'X_CONTENT_TYPE_OPTIONS': 'X-Content-Type-Options',
+            'X_XSS_PROTECTION': 'X-XSS-Protection',
+            'WEB_SERVER': 'Server',
+        }
+
+        # X-Frame-Options for security
+        if config.X_FRAME_OPTIONS != "" and \
+                config.X_FRAME_OPTIONS.lower() != "deny":
+            response.headers["X-Frame-Options"] = config.X_FRAME_OPTIONS
+
+        # add other security options
+        for key in params_dict:
+            if key in config.__dict__ and config.__dict__[key] != "" \
+                    and config.__dict__[key] is not None:
+                response.headers[params_dict[key]] = config.__dict__[key]
diff --git a/web/pgadmin/utils/session.py b/web/pgadmin/utils/session.py
index b8af3d4..62e03ba 100644
--- a/web/pgadmin/utils/session.py
+++ b/web/pgadmin/utils/session.py
@@ -311,7 +311,11 @@ class ManagedSessionInterface(SessionInterface):
         response.set_cookie(
             app.session_cookie_name,
             '%s!%s' % (session.sid, session.hmac_digest),
-            expires=cookie_exp, httponly=True, domain=domain
+            expires=cookie_exp,
+            secure=config.SESSION_COOKIE_SECURE,
+            httponly=config.SESSION_COOKIE_HTTPONLY,
+            samesite=config.SESSION_COOKIE_SAMESITE,
+            domain=domain
         )
 
 
