GIS系统中经纬度参数是地理位置查询的核心输入,很多业务场景会直接将前端传入的经纬度拼接进SQL语句查询对应区域的数据,这种未校验的输入很容易成为SQL注入的突破口。攻击者可以构造包含SQL关键字的经纬度参数,篡改查询逻辑获取敏感地理数据,甚至破坏数据库内容。

GIS经纬度参数的常见风险点
经纬度参数通常出现在区域查询、附近点位检索等场景中,常见的风险输入包括以下几类:
- 参数类型非数值,比如传入字符串
120.123' OR '1'='1 - 数值超出合理范围,比如纬度传入100,经度传入200
- 参数格式不符合规范,比如包含多余字符、小数位异常
- 参数拼接时未使用参数化查询,直接拼接SQL字符串
经纬度参数的格式化检测规则
要防御这类注入风险,首先需要对经纬度参数做严格的格式化检测,校验规则需要覆盖以下几个维度:
1. 参数类型校验
经纬度本质是实数类型,首先需要确认传入的参数可以转换为浮点数,拒绝非数值类型的输入。
2. 数值范围校验
纬度的合理范围是-90到90,经度的合理范围是-180到180,超出这个范围的数值属于非法输入。
3. 格式规范校验
正常的经纬度最多保留6位小数,且不能包含除数字、负号、小数点之外的其他字符,避免出现SQL关键字或特殊符号。
格式化检测的代码实现
以下是Python语言的经纬度参数校验实现示例,可在接口层对传入的经纬度做前置检测:
import re
def validate_coordinate(lat, lng):
"""
校验经纬度参数是否合法
:param lat: 纬度参数
:param lng: 经度参数
:return: 校验结果和错误信息
"""
# 先校验参数是否为空
if lat is None or lng is None:
return False, "经纬度参数不能为空"
# 类型校验:尝试转换为浮点数
try:
lat_float = float(lat)
lng_float = float(lng)
except (ValueError, TypeError):
return False, "经纬度必须为数值类型"
# 范围校验
if not (-90 <= lat_float <= 90):
return False, "纬度范围必须在-90到90之间"
if not (-180 <= lng_float <= 180):
return False, "经度范围必须在-180到180之间"
# 格式校验:只允许数字、负号、小数点,最多6位小数
coord_pattern = re.compile(r'^-?d+(.d{1,6})?$')
if not coord_pattern.match(str(lat)) or not coord_pattern.match(str(lng)):
return False, "经纬度格式不符合规范,最多保留6位小数"
return True, "参数校验通过"
# 测试用例
test_cases = [
("39.908823", "116.397470"), # 合法北京坐标
("100", "116.397470"), # 纬度超范围
("39.908823", "200"), # 经度超范围
("39.908823' OR '1'='1", "116.397470"), # 包含注入字符
("39.90882312345", "116.397470") # 小数位超6位
]
for lat, lng in test_cases:
valid, msg = validate_coordinate(lat, lng)
print(f"lat:{lat}, lng:{lng} 校验结果:{valid}, 信息:{msg}")
结合参数化查询进一步防御
格式化检测只是第一道防线,在执行数据库查询时,必须使用参数化查询,避免将经纬度参数直接拼接进SQL字符串。以下是使用Python的pymysql库执行参数化查询的示例:
import pymysql
def query_nearby_points(lat, lng, radius):
"""
查询指定坐标附近的点位,使用参数化查询避免注入
:param lat: 纬度
:param lng: 经度
:param radius: 查询半径,单位米
"""
# 先校验经纬度参数
valid, msg = validate_coordinate(lat, lng)
if not valid:
raise ValueError(msg)
# 连接数据库
conn = pymysql.connect(
host='127.0.0.1',
user='gis_user',
password='gis_pass',
database='gis_db',
charset='utf8mb4'
)
cursor = conn.cursor()
# 参数化查询,使用%s作为占位符,参数通过元组传入,避免拼接SQL
sql = """
SELECT id, name, lat, lng
FROM geo_points
WHERE ST_Distance_Sphere(
POINT(lng, lat),
POINT(%s, %s)
) <= %s
"""
cursor.execute(sql, (lng, lat, radius))
result = cursor.fetchall()
cursor.close()
conn.close()
return result
总结
防御针对GIS地理位置数据的SQL注入,核心是对经纬度参数做严格的格式化检测,同时配合参数化查询使用。格式化检测可以拦截大部分非法输入,参数化查询则能从SQL执行层面杜绝注入风险,两者结合可以大幅提升GIS系统的数据安全性。在实际开发中,还需要对前端传入的所有参数做统一校验,避免遗漏其他潜在的注入点。