diff --git a/package/gluon-autoupdater/check_site.lua b/package/gluon-autoupdater/check_site.lua
index 1d8996f05505ee46369ce52849b0c320063b5383..9f443fec5ac71cd0f91f50cd231e2257554d25a2 100644
--- a/package/gluon-autoupdater/check_site.lua
+++ b/package/gluon-autoupdater/check_site.lua
@@ -1,6 +1,8 @@
 need_string 'autoupdater.branch'
 
 local function check_branch(k, _)
+   assert_uci_name(k)
+
    local prefix = string.format('autoupdater.branches[%q].', k)
 
    need_string(prefix .. 'name')
diff --git a/package/gluon-mesh-vpn-fastd/check_site.lua b/package/gluon-mesh-vpn-fastd/check_site.lua
index 7a2c4913d64dcac1b2a8228a5e6616489e7e8fa4..7960ea19174edae54680635f7f1544d38040cae7 100644
--- a/package/gluon-mesh-vpn-fastd/check_site.lua
+++ b/package/gluon-mesh-vpn-fastd/check_site.lua
@@ -6,6 +6,8 @@ need_boolean('fastd_mesh_vpn.configurable', false)
 
 local function check_peer(prefix)
   return function(k, _)
+    assert_uci_name(k)
+
     local table = string.format('%s[%q].', prefix, k)
 
     need_string(table .. 'key')
@@ -15,6 +17,8 @@ end
 
 local function check_group(prefix)
   return function(k, _)
+    assert_uci_name(k)
+
     local table = string.format('%s[%q].', prefix, k)
 
     need_number(table .. 'limit', false)
diff --git a/package/gluon-simple-tc/check_site.lua b/package/gluon-simple-tc/check_site.lua
index 95d4fd81c2a92a3cf0a73d8e3a4eaaef13bcce05..3b8ddc534f55a258bde7b35460b8ae2f52ccaad9 100644
--- a/package/gluon-simple-tc/check_site.lua
+++ b/package/gluon-simple-tc/check_site.lua
@@ -1,4 +1,6 @@
 local function check_entry(k, _)
+   assert_uci_name(k)
+
    local prefix = string.format('simple_tc[%q].', k)
 
    need_string(prefix .. 'ifname')
diff --git a/scripts/check_site_lib.lua b/scripts/check_site_lib.lua
index 99489302406451042b53391bbb46f5ae426de854..766b94a94186af83ad4e21b6813356d18610eb3c 100644
--- a/scripts/check_site_lib.lua
+++ b/scripts/check_site_lib.lua
@@ -12,6 +12,12 @@ local function assert_type(var, t, msg)
 end
 
 
+function assert_uci_name(var)
+   -- We don't use character classes like %w here to be independent of the locale
+   assert(var:match('^[0-9a-zA-Z_]+$'), "site.conf error: `" .. var .. "' is not a valid config section name (only alphanumeric characters and the underscore are allowed)")
+end
+
+
 function need_string(varname, required)
    local var = loadvar(varname)