diff --git a/Dockerfile b/Dockerfile
index ee761c23557dcc65ba1fa5ad42cc2f7232c842fe..8419d0d425fe819b22782049aa95c86f7ff66d05 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,6 +20,8 @@ RUN DATABASE_URL=postgres://x/x \
     OIDC_RP_REALM_URL=x \
     OIDC_RP_CLIENT_ID=x \
     OIDC_RP_CLIENT_SECRET=x \
+    CLAMD_TCP_SOCKET=x \
+    CLAMD_TCP_ADDR=x \
     DEFAULT_COUNTRY=x \
     DEFAULT_CONTRACTEE_NAME=x \
     DEFAULT_CONTRACTEE_STREET=x \
diff --git a/contracts/migrations/0032_alter_signee_address_country.py b/contracts/migrations/0032_alter_signee_address_country.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4158bc3cca6dceb00864f692cd021dbadcb5de9
--- /dev/null
+++ b/contracts/migrations/0032_alter_signee_address_country.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.1.4 on 2023-04-03 21:35
+
+import contracts.models
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contracts', '0031_alter_contract_cost_unit_other_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='signee',
+            name='address_country',
+            field=models.CharField(blank=True, default=contracts.models.get_default_country, max_length=256, null=True, verbose_name='Země'),
+        ),
+    ]
diff --git a/contracts/models.py b/contracts/models.py
index 8fa16d2950392aea239e8efebd343140e0e1e2d7..ca97769da3939c7dbd8d9236a14aa17c13ac5270 100644
--- a/contracts/models.py
+++ b/contracts/models.py
@@ -81,6 +81,10 @@ class RepresentativeMixin:
         return result
 
 
+def get_default_country():
+    return settings.DEFAULT_COUNTRY
+
+
 class Signee(CreatedByMixin, OwnPermissionsMixin, SignatureCountMixin, models.Model):
     name = models.CharField(
         max_length=256,
@@ -129,7 +133,7 @@ class Signee(CreatedByMixin, OwnPermissionsMixin, SignatureCountMixin, models.Mo
         blank=True,
         null=True,
         verbose_name="Země",
-        default=settings.DEFAULT_COUNTRY,
+        default=get_default_country,
     )
 
     ico_number = models.CharField(
diff --git a/env.example b/env.example
index 38e326158c3f703a3db62b30070ee5f73507e5e3..797ac95162181c06256e0ad1eb8ae301f29546c3 100644
--- a/env.example
+++ b/env.example
@@ -10,6 +10,9 @@ OIDC_RP_CLIENT_SECRET=VCn4LVAUc6RGLSup7VaAKsmrKUbWguaP
 
 DEFAULT_COUNTRY="Česká Republika"
 
+CLAMD_TCP_SOCKET=3310
+CLAMD_TCP_ADDR=127.0.0.1
+
 DEFAULT_CONTRACTEE_NAME="Česká pirátská strana"
 DEFAULT_CONTRACTEE_STREET="Na Moráni 360/3"
 DEFAULT_CONTRACTEE_ZIP="128 00"
diff --git a/registry/settings/base.py b/registry/settings/base.py
index e4da047817f8374fa8f90347816e334394286302..380f476a8e3ddcd712b73117bcb3bc52b5d8a51c 100644
--- a/registry/settings/base.py
+++ b/registry/settings/base.py
@@ -70,14 +70,14 @@ INSTALLED_APPS = [
 MIDDLEWARE = [
     "django.middleware.security.SecurityMiddleware",
     "django.contrib.sessions.middleware.SessionMiddleware",
+    "django.contrib.auth.middleware.AuthenticationMiddleware",
     "django.middleware.common.CommonMiddleware",
     "django.middleware.csrf.CsrfViewMiddleware",
-    "django.contrib.auth.middleware.AuthenticationMiddleware",
     "django.contrib.messages.middleware.MessageMiddleware",
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
     "django_http_exceptions.middleware.ExceptionHandlerMiddleware",
     "django_http_exceptions.middleware.ThreadLocalRequestMiddleware",
-    # "django_downloadview.SmartDownloadMiddleware",
+    "shared.middlewares.ClamAVMiddleware"
 ]
 
 ROOT_URLCONF = "registry.urls"
@@ -216,6 +216,13 @@ ADMIN_ORDERING = {
 }
 
 
+# ClamAV
+
+CLAMD_USE_TCP = True
+CLAMD_TCP_SOCKET = env.int("CLAMD_TCP_SOCKET")
+CLAMD_TCP_ADDR = env.str("CLAMD_TCP_ADDR")
+
+
 ## App-specific
 
 DEFAULT_CONTRACTEE_NAME = env.str("DEFAULT_CONTRACTEE_NAME")
diff --git a/requirements/base.txt b/requirements/base.txt
index dc65d55174714f9d285f0fede46f87f174af041d..d4143b6dc710f1364c1a0bf1afe16654745a2cee 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -1,3 +1,4 @@
+clamd==1.0.2
 django==4.1.4
 django-admin-index==2.0.2
 django-admin-interface==0.24.2
diff --git a/shared/middlewares.py b/shared/middlewares.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd3058c82f059444c72cc01ec4dada9fc64d2108
--- /dev/null
+++ b/shared/middlewares.py
@@ -0,0 +1,31 @@
+import clamd
+
+from io import BytesIO
+from django.http import HttpResponseForbidden
+
+class ClamAVMiddleware:
+    def __init__(self, get_response):
+        self.get_response = get_response
+        # One-time configuration and initialization.
+
+    def __call__(self, request):
+        # Code to be executed for each request before
+        # the view (and later middleware) are called.
+
+        cd = clamd.ClamdUnixSocket()
+
+        if request.method == "POST" and len(request.FILES) > 0:
+            for file_ in request.FILES.values():
+                scan_result = cd.instream(BytesIO(file_.read()))
+
+                if scan_result["stream"][0] == "FOUND":
+                    return HttpResponseForbidden(
+                        "Nahraný soubor obsahuje potenciálně škodlivý obsah."
+                    )
+
+        response = self.get_response(request)
+
+        # Code to be executed for each request/response after
+        # the view is called.
+
+        return response